package cc.mrbird.febs.pay.service.impl; import cc.mrbird.febs.common.properties.XcxProperties; import cc.mrbird.febs.common.utils.SpringContextHolder; import cc.mrbird.febs.pay.service.WxFaPiaoService; import cc.mrbird.febs.pay.util.RandomStringGenerator; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import okhttp3.HttpUrl; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; import org.springframework.util.Base64Utils; import java.io.*; import java.nio.charset.StandardCharsets; import java.security.*; import java.security.cert.X509Certificate; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; @Slf4j @Service @RequiredArgsConstructor public class WxFaPiaoServiceImpl implements WxFaPiaoService { private final XcxProperties xcxProperties = SpringContextHolder.getBean(XcxProperties.class); @Override public String createAuthorization(String method, String canonicalUrl, String body, KeyPair keyPair) { String nonceStr = RandomStringGenerator.getRandomStringByLength(32);//随机字符串 long timestamp = System.currentTimeMillis() / 1000;//时间戳 String signature = sign(method, canonicalUrl, timestamp, nonceStr, body, keyPair);//签名加密 return "mchid=\"" + xcxProperties.getWecharpayMchid() + "\"," + "nonce_str=\"" + nonceStr + "\"," + "timestamp=\"" + timestamp + "\"," + "serial_no=\"" + "50F37206347BCC9E6AC9860DAACE52AC035F7C24" + "\","//证书序列号 + "signature=\"" + signature + "\""; } @Override public KeyPair getPrivateKey() { return createPKCS12("Tenpay Certificate", "1658958205"); } /** * V3 SHA256withRSA 签名. * * @param method 请求方法 GET POST PUT DELETE 等 * @param canonicalUrl 例如 https://api.mch.weixin.qq.com/v3/pay/transactions/app?version=1 ——> /v3/pay/transactions/app?version=1 * @param timestamp 当前时间戳 因为要配置到TOKEN 中所以 签名中的要跟TOKEN 保持一致 * @param nonceStr 随机字符串 要和TOKEN中的保持一致 * @param body 请求体 GET 为 "" POST 为JSON * @param keyPair 商户API 证书解析的密钥对 实际使用的是其中的私钥 * @return the string */ @SneakyThrows public String sign(String method, String canonicalUrl, long timestamp, String nonceStr, String body, KeyPair keyPair) { String signatureStr = Stream.of(method, canonicalUrl, String.valueOf(timestamp), nonceStr, body) .collect(Collectors.joining("\n", "", "\n")); Signature sign = Signature.getInstance("SHA256withRSA"); sign.initSign(keyPair.getPrivate()); sign.update(signatureStr.getBytes(StandardCharsets.UTF_8)); return Base64Utils.encodeToString(sign.sign()); } /** * 获取公私钥.通过证书 */ private KeyStore store; private final Object lock = new Object(); public KeyPair createPKCS12(String keyAlias, String keyPass) { ClassPathResource resource = new ClassPathResource(xcxProperties.getCertLocalPath()); // File file = new File("src/main/resources/wxP12/apiclient_cert.p12"); char[] pem = keyPass.toCharArray(); try { synchronized (lock) { if (store == null) { synchronized (lock) { store = KeyStore.getInstance("PKCS12"); store.load(resource.getInputStream(), pem); // store.load(new FileInputStream(file), pem); } } } 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); } } public static void main(String[] args) { try { System.out.println(new ClassPathResource("wxP12/apiclient_cert.p12").getFile().exists()); } catch (IOException e) { e.printStackTrace(); } } }