分享一下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); } }
文章评论