企业付款到银行卡其实就是企业付款到到零钱的进价版,只不过比到零钱多了一个RSA加密.只要加密好了,根据官方提供的参数,直接请求即可.
首先就是通过官方提供的接口https://fraud.mch.weixin.qq.com/risk/getpublickey获取RSA 公钥,也就是pub_key这个字段.
直接上代码
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import java.io.DataInputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;
public class Rsa {
//获取RSA加密秘钥接口路径
private static final String RSA = "https://fraud.mch.weixin.qq.com/risk/getpublickey";
private static final Logger log = LoggerFactory.getLogger("adminLogger");
public static void main(String[] args) {
Map<String, String> params = new HashMap<>();
String nonce_str = getUUID();
params.put("mch_id", "商户号");
params.put("nonce_str", nonce_str);
params.put("sign_type", "MD5");
params.put("sign", createSign(params, "商户秘钥"));
String result = null;
try {
result = httpRequestTransfer(RSA, toXml(params), "商户号", "证书路径(http地址类型)");
} catch (Exception e) {
e.printStackTrace();
}
log.error("微信请求企业付款接口返回:" + result);
}
private static String getUUID(){
return UUID.randomUUID().toString().replace("-", "");
}
private static String createSign(Map<String, String> params, String paternerKey) {
// 生成签名前先去除sign
params.remove("sign");
String stringA = packageSign(params, false);
String stringSignTemp = stringA + "&key=" + paternerKey;
return DigestUtils.md5Hex(stringSignTemp).toUpperCase();
}
private static String packageSign(Map<String, String> params, boolean urlEncoder) {
// 先将参数以其参数名的字典序升序进行排序
TreeMap<String, String> sortedParams = new TreeMap<String, String>(params);
// 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起
StringBuilder sb = new StringBuilder();
boolean first = true;
for (Map.Entry<String, String> param : sortedParams.entrySet()) {
String value = param.getValue();
if (StrKit.isBlank(value)) {
continue;
}
if (first) {
first = false;
} else {
sb.append("&");
}
sb.append(param.getKey()).append("=");
if (urlEncoder) {
try {
value = urlEncode(value);
} catch (UnsupportedEncodingException e) {
}
}
sb.append(value);
}
return sb.toString();
}
private static String urlEncode(String src) throws UnsupportedEncodingException {
return URLEncoder.encode(src, "UTF-8").replace("+", "%20");
}
private static String httpRequestTransfer(String url, String data, String mch_id, String path) throws Exception {
//证书类型
KeyStore keyStore = KeyStore.getInstance("PKCS12");
URL urlPro = new URL(path);
DataInputStream instream = new DataInputStream(urlPro.openStream());
try {
//指定PKCS12的密码(商户ID)
keyStore.load(instream, mch_id.toCharArray());
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mch_id.toCharArray()).build();
//指定TLS版本
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext, new String[]{"TLSv1"}, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
//设置httpclient的SSLSocketFactory
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
try {
HttpPost httpost = new HttpPost(url); // 设置响应头信息
httpost.addHeader("Accept", "*/*");
httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
httpost.setEntity(new StringEntity(data, "UTF-8"));
CloseableHttpResponse response = httpclient.execute(httpost);
try {
HttpEntity entity = response.getEntity();
String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
EntityUtils.consume(entity);
return jsonStr;
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
private static String toXml(Map<String, String> params) {
StringBuilder xml = new StringBuilder();
xml.append("<xml>");
for (Map.Entry<String, String> entry : params.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
// 略过空值
if (StrKit.isBlank(value)) continue;
xml.append("<").append(key).append(">");
xml.append(entry.getValue());
xml.append("</").append(key).append(">");
}
xml.append("</xml>");
return xml.toString();
}
private static Map<String, String> xmlToMap(String xmlStr) {
XmlHelper xmlHelper = XmlHelper.of(xmlStr);
return xmlHelper.toMap();
}
}
然后把获取到的rsa秘钥保存到本地,修改文件名称以及类型
注意保存的时候不要去掉-----BEGIN RSA PUBLIC KEY----- 和 -----END PUBLIC KEY-----
这个秘钥是PKCS#1格式的,java的话要使用PKCS#8的,文档提供了互转的方法
openssl rsa -RSAPublicKey_in -in <filename> -pubout 用dos命令指令 filename为文件全路径,注意要去掉尖括号
然后把新秘钥也保存到本地
有了秘钥,下面可以进行加密操作,加密成功后基本上就大功告成了
以下是加密方法
public static void main(String[] args)throws Exception {
//待加密数据
String card = "123456789";
//获取PublicKey对象 第一个参数为rsa公钥地址,这里我把公钥放在了远程服务器上
PublicKey pub = RSAUtil.getPubKey("https://xxx.xxxx.com/pksc8_public.pem", "RSA");
//获取加密后数据 后三个参数为固定值(别问为啥,别人那里看的,最后一个参数为填充方式)
String encode = Base64.encode(RSAUtil.encrypt(card.getBytes(), pub, 2048, 11, "RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING"));
}
最后是RSAUtil
import javax.crypto.Cipher;
import java.io.*;
import java.lang.reflect.Method;
import java.net.URL;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
public class RSAUtil {
public static byte[] encrypt(byte[] plainBytes, PublicKey publicKey, int keyLength, int reserveSize, String cipherAlgorithm) throws Exception {
int keyByteSize = keyLength / 8;
int encryptBlockSize = keyByteSize - reserveSize;
int nBlock = plainBytes.length / encryptBlockSize;
if ((plainBytes.length % encryptBlockSize) != 0) {
nBlock += 1;
}
ByteArrayOutputStream outbuf = null;
try {
Cipher cipher = Cipher.getInstance(cipherAlgorithm);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
outbuf = new ByteArrayOutputStream(nBlock * keyByteSize);
for (int offset = 0; offset < plainBytes.length; offset += encryptBlockSize) {
int inputLen = plainBytes.length - offset;
if (inputLen > encryptBlockSize) {
inputLen = encryptBlockSize;
}
byte[] encryptedBlock = cipher.doFinal(plainBytes, offset, inputLen);
outbuf.write(encryptedBlock);
}
outbuf.flush();
return outbuf.toByteArray();
} catch (Exception e) {
throw new Exception("ENCRYPT ERROR:", e);
} finally {
try {
if (outbuf != null) {
outbuf.close();
}
} catch (Exception e) {
outbuf = null;
throw new Exception("CLOSE ByteArrayOutputStream ERROR:", e);
}
}
}
public static PublicKey getPubKey(String publicKeyPath, String keyAlgorithm) {
PublicKey publicKey = null;
InputStream inputStream = null;
try {
URL urlPro = new URL(publicKeyPath);
inputStream = new DataInputStream(urlPro.openStream());
publicKey = getPublicKey(inputStream, keyAlgorithm);
} catch (Exception e) {
e.printStackTrace();//EAD PUBLIC KEY ERROR
System.out.println("加载公钥出错!");
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception e) {
System.out.println("加载公钥,关闭流时出错!");
}
}
}
return publicKey;
}
public static PublicKey getPublicKey(InputStream inputStream, String keyAlgorithm) throws Exception {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder sb = new StringBuilder();
String readLine = null;
while ((readLine = br.readLine()) != null) {
if (readLine.charAt(0) == '-') {
continue;
} else {
sb.append(readLine);
sb.append('\r');
}
}
X509EncodedKeySpec pubX509 = new X509EncodedKeySpec(decodeBase64(sb.toString()));
KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
//下行出错 java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: DerInputStream.getLength(): lengthTag=127, too big.
PublicKey publicKey = keyFactory.generatePublic(pubX509);
return publicKey;
} catch (Exception e) {
e.printStackTrace();
throw new Exception("READ PUBLIC KEY ERROR:", e);
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
inputStream = null;
throw new Exception("INPUT STREAM CLOSE ERROR:", e);
}
}
}
/***
* decode by Base64
*/
public static byte[] decodeBase64(String input) throws Exception {
Class clazz = Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");
Method mainMethod = clazz.getMethod("decode", String.class);
mainMethod.setAccessible(true);
Object retObj = mainMethod.invoke(null, input);
return (byte[]) retObj;
}
最后,绑定参数请求微信提供的接口即可