KKSU
2024-01-16 15d223c549e0d94316db344f2f6337b605fffbae
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
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();
        }
    }
 
}