[Java]实现SHA256withRSA签名(可用户微信支付APIv3签名)

2022-02-23 2215点热度 0人点赞 0条评论

分享一下Java下使用SHA256withRSA签名的代码,例如微信支付APIv3中的签名,就是使用的SHA256 with RSA签名,并对结果进行base64的编码。

这里签名使用到的密钥文件,是使用了Spring的特性,通过@Value(value = "${my.wx.mchApiClientKey}")加载进来的,可以根据使用实际需求修改。

通过generateSignature函数,对要求的参数(数组)进行签名。

WXPayUtil.java 如下,在SpringBoot下我加了@Component注解,并使用@Valuec从配置文件中加载所需要的参数(例如密钥文件内容等)

@Component
public class WXPayUtil {

    @Value(value = "${my.wx.mchId}")
    private String mchId;

    @Value(value = "${my.wx.mchKey}")
    private String mchKey;

    @Value(value = "${my.wx.mchSerial}")
    private String mchSerial;

    // 签名使用的私钥文本内容,从配置文件application.yml中加载my.wx.mchApiClientKey的值
    @Value(value = "${my.wx.mchApiClientKey}")
    private String mchApiClientKey;

    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private static final Random RANDOM = new SecureRandom();

    public String apiPostJson(String urlSuffix, String json) throws Exception {
        String nonce = generateNonceStr();
        String timestamp = getCurrentTimestamp();
        // 微信支付要求对参数组成数组,并使用\n进行拼接(最后一行也要加\n,然后进行签名)
        String signature = generateSignature(new String[]{"POST", urlSuffix, timestamp, nonce, json});
        RequestBody body = RequestBody.create(MediaType.get("application/json; charset=utf-8"), json);
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url(WXPayConstants.DOMAIN_API + urlSuffix)
                .header("User-Agent", WXPayConstants.USER_AGENT)
                .header("Accept", "application/json")
                .header("Authorization", "WECHATPAY2-SHA256-RSA2048 "
                        + "mchid=\"" + mchId + "\",nonce_str=\"" + nonce + "\"" + ",signature=\"" + signature + "\""
                        + ",timestamp=\"" + timestamp + "\"" + ",serial_no=\"" + mchSerial + "\"")
                .post(body).build();
        Response response = client.newCall(request).execute();
        return response.body().string();
    }

    public String apiGet(String urlSuffix) throws Exception {
        String nonce = generateNonceStr();
        String timestamp = getCurrentTimestamp();
        String signature = generateSignature(new String[]{"GET", urlSuffix, timestamp, nonce, ""});
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url(WXPayConstants.DOMAIN_API + urlSuffix)
                .header("User-Agent", WXPayConstants.USER_AGENT)
                .header("Accept", "application/json")
                .header("Authorization", "WECHATPAY2-SHA256-RSA2048 "
                        + "mchid=\"" + mchId + "\",nonce_str=\"" + nonce + "\"" + ",signature=\"" + signature + "\""
                        + ",timestamp=\"" + timestamp + "\"" + ",serial_no=\"" + mchSerial + "\"")
                .get().build();
        Response response = client.newCall(request).execute();
        return response.body().string();
    }

    /**
     * 从文本中加载私钥
     */
    public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception {
//        PKCS#1 format 格式的私钥会报错,需要假如如下addProvider,maven需要添加如下dependency
//        <dependency>
//            <groupId>org.bouncycastle</groupId>
//            <artifactId>bcprov-jdk15on</artifactId>
//            <version>1.58</version>
//        </dependency>
        java.security.Security.addProvider(
                new org.bouncycastle.jce.provider.BouncyCastleProvider()
        );
        String string = privateKeyStr
                .replace("-----BEGIN RSA PRIVATE KEY-----", "")
                .replace("-----END RSA PRIVATE KEY-----", "")
                .replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "")
                .replaceAll("\\s", "");
        byte[] buffer = base64Decode(string);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * 生成签名.
     *
     * @param data 待签名数据
     * @return 签名
     */
    public String generateSignature(final String[] data) throws Exception {
        PrivateKey privateKey = loadPrivateKey(mchApiClientKey);

        Signature privateSignature = Signature.getInstance("SHA256withRSA");
        privateSignature.initSign(privateKey);
        privateSignature.update((String.join("\n", data) + "\n").getBytes(StandardCharsets.UTF_8));
        byte[] s = privateSignature.sign();
        return base64Encode(s);
    }


    /**
     * 获取随机字符串 Nonce Str
     *
     * @return String 随机字符串
     */
    public static 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);
    }

    public static String base64Encode(byte[] data) {
        return Base64.getEncoder().encodeToString(data);
    }

    public static byte[] base64Decode(String data) {
        return Base64.getDecoder().decode(data);
    }

    /**
     * 获取当前时间戳,单位秒
     */
    public static String getCurrentTimestamp() {
        return String.valueOf(System.currentTimeMillis() / 1000);
    }

}

 

admin

这个人很懒,什么都没留下

文章评论

您需要 登录 之后才可以评论