首先根据微信提供的源码将平台证书下载下来https://github.com/wechatpay-apiv3/CertificateDownloader,注意第一次下载平台证书的时候去掉CertificateDownloaderTest代码里面的 "-c", wechatpayCertificateFilePath
图片上传:
public static String uploadApplyment(String filePath, String mchid, String serial_no, String rsaPrivateKey, String nonce_str,String timestamp) {
String result=null;
try {
File file =new File(filePath);
String filename = file.getName();//文件名
String fileSha256 = DigestUtils.sha256Hex(new FileInputStream(file));//文件sha256
//拼签名串
StringBuffer sb =new StringBuffer();
sb.append("POST").append("\n");
sb.append("/v3/merchant/media/upload").append("\n");
sb.append(timestamp).append("\n");
sb.append(nonce_str).append("\n");
sb.append("{\"filename\":\""+filename+"\",\"sha256\":\""+fileSha256+"\"}").append("\n");
System.out.println("签名原串:"+sb.toString());
//计算签名
String sign =new String(Base64.encodeBase64(signRSA(sb.toString(),rsaPrivateKey)));
System.out.println("签名sign值:"+sign);
//拼装http头的Authorization内容
String authorization ="WECHATPAY2-SHA256-RSA2048 mchid=\""+mchid+"\",nonce_str=\""+nonce_str+"\",signature=\""+sign+"\",timestamp=\""+timestamp+"\",serial_no=\""+serial_no+"\"";
System.out.println("authorization值:"+authorization);
//接口URL https://api.mch.weixin.qq.com/v3/merchant/media/upload
String url ="https://api.mch.weixin.qq.com/v3/merchant/media/upload";
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPost httpPost =new HttpPost(url);
//设置头部
httpPost.addHeader("Accept","application/json");
httpPost.addHeader("Content-Type","multipart/form-data");
httpPost.addHeader("Authorization", authorization);
//创建MultipartEntityBuilder
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create().setMode(HttpMultipartMode.RFC6532);
//设置boundary
multipartEntityBuilder.setBoundary("");
multipartEntityBuilder.setCharset(Charset.forName("UTF-8"));
multipartEntityBuilder.setBoundary("boundary");
//设置meta内容
multipartEntityBuilder.addTextBody("meta","{\"filename\":\""+filename+"\",\"sha256\":\""+fileSha256+"\"}", ContentType.APPLICATION_JSON);
//设置图片内容
multipartEntityBuilder.addBinaryBody("file", file, ContentType.create("image/jpg"), filename);
//放入内容
httpPost.setEntity(multipartEntityBuilder.build());
//获取返回内容
CloseableHttpResponse response = httpclient.execute(httpPost);
HttpEntity httpEntity = response.getEntity();
String rescontent =new String(InputStreamTOByte(httpEntity.getContent()));
System.out.println("返回内容:" + rescontent);
}catch (Exception e) {
System.out.println("发送POST请求异常!" + e);
e.printStackTrace();
}
return result;
}
public static byte[] InputStreamTOByte(InputStream in)throws IOException{
int BUFFER_SIZE =4096;
ByteArrayOutputStream outStream =new ByteArrayOutputStream();
byte[] data =new byte[BUFFER_SIZE];
int count = -1;
while((count = in.read(data,0,BUFFER_SIZE)) != -1)
outStream.write(data,0, count);
data =null;
byte[] outByte = outStream.toByteArray();
outStream.close();
return outByte;
}
public static byte[] signRSA(String data, String priKey) throws Exception {
//签名的类型
Signature sign = Signature.getInstance("SHA256withRSA");
//读取商户私钥,该方法传入商户私钥证书的内容即可
byte[] keyBytes = Base64.decodeBase64(priKey);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
sign.initSign(privateKey);
sign.update(data.getBytes("UTF-8"));
return sign.sign();
}
接下来是进件和查询的代码
public class WechartTest {
/***
查询申请单
*/
@Test
public void test() throws Exception {
String url = "https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/applyment_id/123456";
String merchantId = "";
String serialNo = WeChatUtil.getSerialNo(WeChatUtil.apiclient_cert);
String token = WeChatUtil.getToken("GET", url, null, merchantId, serialNo);
String authorization = "WECHATPAY2-SHA256-RSA2048 " + token;
// 平台证书路径 是必须的
serialNo = WeChatUtil.getSerialNo(WeChatUtil.publicKeyPath);
HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("Wechatpay-Serial", serialNo);
httpGet.setHeader("Accept", "application/json");
httpGet.setHeader("Content-Type", "application/json");
httpGet.setHeader("user-agent", WeChatUtil.DEFAULT_USER_AGENT);
httpGet.setHeader("Authorization", authorization);
HttpClientBuilder httpClientBuilder = HttpClients.custom();
CloseableHttpClient httpClient = httpClientBuilder.build();
CloseableHttpResponse httpResponse = httpClient.execute(httpGet);
HttpEntity httpResponseEntity = httpResponse.getEntity();
String responseEntityStr = EntityUtils.toString(httpResponseEntity);
httpResponse.close();
System.out.println(responseEntityStr);
}
/***
进件接口
*/
@Test
public void test2() throws Exception {
String url = "https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/";
// TODO
String merchantId = "";
String serialNo = WeChatUtil.getSerialNo(WeChatUtil.apiclient_cert);
Map<String,Object> requestMap = new HashMap<>(1);
requestMap.put("business_code","业务申请编号");
// 参数自己根据文档进行组装
String requestParams = JSONObject.toJSONString(requestMap);
String token = WeChatUtil.getToken("POST", url, requestParams, merchantId, serialNo);
String authorization = "WECHATPAY2-SHA256-RSA2048 " + token;
// 平台证书路径 是必须的
serialNo = WeChatUtil.getSerialNo(WeChatUtil.publicKeyPath);
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("Wechatpay-Serial", serialNo);
httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("Content-Type", "application/json");
httpPost.setHeader("user-agent", WeChatUtil.DEFAULT_USER_AGENT);
httpPost.setHeader("Authorization", authorization);
httpPost.setEntity(new StringEntity(requestParams, "UTF-8"));
HttpClientBuilder httpClientBuilder = HttpClients.custom();
CloseableHttpClient httpClient = httpClientBuilder.build();
CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpResponseEntity = httpResponse.getEntity();
String responseEntityStr = EntityUtils.toString(httpResponseEntity);
httpResponse.close();
System.out.println(responseEntityStr);
}
/**
* 敏感字加密
*
* @throws IOException
*/
@Test
public void test1() throws IOException, IllegalBlockSizeException {
X509Certificate certificate = WeChatUtil.getCertificate(WeChatUtil.publicKeyPath);
Cipher cipher = WeChatUtil.getCipher(certificate);
String s = WeChatUtil.rsaEncryptOAEP("张三", cipher);
System.out.println(s);
}
}
public class WeChatUtil {
public static final String DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36";
// 商户证书路径
public static final String apiclient_cert = "apiclient_cert.pem";
// 平台证书路径
public static final String publicKeyPath = "wechatpay.pem";
// 商户私钥路径
public static final String privageKeyPath = "apiclient_key.pem";
/**
* 获取证书序列号
*
* @param certPath 获取商户证书序列号 传递商号证书路径 apiclient_cert
* @return
* @throws IOException
*/
public static String getSerialNo(String certPath) throws IOException {
X509Certificate certificate = getCertificate(certPath);
return certificate.getSerialNumber().toString(16).toUpperCase();
}
/**
* 获取证书。
*
* @param filename 证书文件路径 (required)
* @return X509证书
*/
public static X509Certificate getCertificate(String filename) throws IOException {
InputStream fis = new FileInputStream(filename);
try (BufferedInputStream bis = new BufferedInputStream(fis)) {
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
cert.checkValidity();
return cert;
} catch (CertificateExpiredException e) {
throw new RuntimeException("证书已过期", e);
} catch (CertificateNotYetValidException e) {
throw new RuntimeException("证书尚未生效", e);
} catch (CertificateException e) {
throw new RuntimeException("无效的证书文件", e);
}
}
/**
* 获取签名
* method(请求类型GET、POST url(请求url)
* body(请求body,GET请求时body传"",POST请求时body为请求参数的json串)
* merchantId(商户号)
* certSerialNo(API证书序列号)
* keyPath(API证书路径)
*
* @param method 请求方式
* @param url 请求路径
* @param body 请求参数
* @param merchantId 商户号
* @param certSerialNo API证书序列号
* @return
* @throws Exception
*/
public static String getToken(String method, String url, String body, String merchantId, String certSerialNo) throws Exception {
String signStr = "";
HttpUrl httpurl = HttpUrl.parse(url);
String nonceStr = UUID.randomUUID().toString().replaceAll("-", "");
long timestamp = System.currentTimeMillis() / 1000;
if (StringUtils.isEmpty(body)) {
body = "";
}
assert httpurl != null;
String message = buildMessage(method, url, timestamp, nonceStr, body);
String signature = sign(message.getBytes(StandardCharsets.UTF_8), privageKeyPath);
signStr = "mchid=\"" + merchantId
+ "\",nonce_str=\"" + nonceStr
+ "\",timestamp=\"" + timestamp
+ "\",serial_no=\"" + certSerialNo
+ "\",signature=\"" + signature + "\"";
return signStr;
}
public static String buildMessage(String method, String url, long timestamp, String nonceStr, String body) {
// String canonicalUrl = url.encodedPath();
// if (url.encodedQuery() != null) {
// canonicalUrl += "?" + url.encodedQuery();
// }
return method + "\n"
+ url + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ body + "\n";
}
public static String sign(byte[] message, String keyPath) throws Exception {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(getPrivateKey(keyPath));
sign.update(message);
return Base64.encodeBase64String(sign.sign());
}
/**
* 获取私钥。
*
* @param filename 私钥文件路径 (required)
* @return 私钥对象
*/
public static PrivateKey getPrivateKey(String filename) throws IOException {
String content = new String(Files.readAllBytes(Paths.get(filename)), StandardCharsets.UTF_8);
try {
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(
new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持RSA", e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("无效的密钥格式");
}
}
public static Cipher getCipher(X509Certificate certificate) {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());
return cipher;
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
} catch (InvalidKeyException e) {
throw new IllegalArgumentException("无效的证书", e);
}
}
/**
* 加密
*
* @param message
* @param cipher 通过getcipher方法获取
* @return
* @throws IllegalBlockSizeException
*/
public static String rsaEncryptOAEP(String message, Cipher cipher) throws IllegalBlockSizeException {
System.out.println("message:"+message);
try {
if (StringUtils.isBlank(message)) {
return null;
}
byte[] data = message.getBytes(StandardCharsets.UTF_8);
byte[] cipherdata = cipher.doFinal(data);
return Base64.encodeBase64String(cipherdata);
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
}
}
}
自己开发的时候遇到了很多问题查找了很多资料 ,希望大家不要踩雷