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