1、介绍
微信支付平台证书是指由微信支付负责申请的,包含微信支付平台标识、公钥信息的证书。商户可以使用平台证书中的公钥进行验签。注意,这里的证书区别于商户API证书,商户API证书是直接从商户后台下载查看的,而微信支付平台证书是通过电商收付通的证书接口获取的。
2、作用
这个证书有什么用?我们需要获取到这个证书相关的序列号和公钥,后续请求一系列接口时需要将微信支付平台证书序列号放在请求头里,而公钥是为了验证应答或回调的签名,以确保应答或回调是由微信支付发送。
3、获取微信支付平台证书
注意:不同的商户,对应的微信支付平台证书是不一样的,平台证书会周期性更换。建议商户定时通过API下载新的证书,不要依赖人工更换证书。微信支付的平台证书序列号位于HTTP头Wechatpay-Serial。验证签名前,请商户先检查序列号是否跟商户当前所持有的微信支付平台证书的序列号一致。如果不一致,请重新获取证书。否则,签名的私钥和证书不匹配,将无法成功验证签名。双手奉上获取微信支付。
* @description
*/
public class Certificate {
private static final Logger logger = LoggerFactory.getLogger(Certificate.class);
/**
* 获取微信支付平台证书
* @param merchantId
* @param timeout
* @param serialNo
* @param mchPrivateKeyPath
* @param APIv3Key
* @param savePath
* @return
*/
public static List<X509Certificate> getCertByAPI(String merchantId, int timeout, String serialNo, String mchPrivateKeyPath,String wechatPubKeyPath,String APIv3Key,String savePath) {
String result = "";
//创建http请求
HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com
/v3/certificates");
httpGet.addHeader("Content-Type", "application/json");
httpGet.addHeader("Accept", "application/json");
String authorization = SignUtils.authorization("GET", "/v3/certificates", merchantId, serialNo, "", mchPrivateKeyPath);
//设置认证信息
httpGet.setHeader("Authorization", authorization);
//设置请求器配置:如超时限制等
RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout * 1000).setConnectTimeout(timeout * 1000).build();
httpGet.setConfig(config);
List<X509Certificate> x509Certs = new ArrayList<X509Certificate>();
try {
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = httpClient.execute(httpGet);
int statusCode = response.getStatusLine().getStatusCode();
HttpEntity httpEntity = response.getEntity();
result = EntityUtils.toString(httpEntity, "UTF-8");
if(statusCode == 200){
logger.info("下载平台证书返回结果:"+result);
Header[] timestampHeader = response.getHeaders("Wechatpay-Timestamp");
Header[] nonceHeader = response.getHeaders("Wechatpay-Nonce");
Header[] signatureHeader = response.getHeaders("Wechatpay-Signature");
if (timestampHeader != null && timestampHeader.length > 0 &&
nonceHeader != null && nonceHeader.length > 0 &&
signatureHeader != null && signatureHeader.length > 0) {
// 验证微信支付返回签名
String wTimestamp = timestampHeader[0].getValue();
String wNonce = nonceHeader[0].getValue();
String wSign = signatureHeader[0].getValue();
logger.info("wTimestamp:{},wNonce:{},wSign:{}", wTimestamp, wNonce, wSign);
// 拼装待签名串
StringBuffer ss = new StringBuffer();
ss.append(wTimestamp).append("\n");
ss.append(wNonce).append("\n");
ss.append(result).append("\n");
// 验证签名
if (SignUtils.v3VerifyRSA(ss.toString(), Base64.decodeBase64(wSign.getBytes()), wechatPubKeyPath)) {
List<CertificateItem> certList = new ArrayList<CertificateItem>();
JSONObject json = JSONObject.parseObject(result);
logger.info("查询结果json字符串转证书List:" + json.get("data"));
JSONArray jsonArray = (JSONArray) json.get("data");
for (int i = 0; i < jsonArray.size(); i++) {
CertificateItem certificateItem = new CertificateItem();
EncryptedCertificateItem encryptCertificate = new EncryptedCertificateItem();
JSONObject bo = JSONObject.parseObject(jsonArray.get(i).toString());
certificateItem.setSerial_no(bo.get("serial_no").toString());
certificateItem.setEffective_time(bo.get("effective_time").toString());
certificateItem.setExpire_time(bo.get("expire_time").toString());
JSONObject encryptBo = JSONObject.parseObject(bo.get("encrypt_certificate").toString());
encryptCertificate.setAlgorithm(encryptBo.get("algorithm").toString());
encryptCertificate.setNonce(encryptBo.get("nonce").toString());
encryptCertificate.setAssociated_data(encryptBo.get("associated_data").toString());
encryptCertificate.setCiphertext(encryptBo.get("ciphertext").toString());
certificateItem.setEncrypt_certificate(encryptCertificate);
certList.add(certificateItem);
}
logger.info("证书List:" + certList);
List<PlainCertificateItem> plainList = decrypt(certList, APIv3Key);
if (CollectionUtils.isNotEmpty(plainList)) {
logger.info("平台证书开始保存");
x509Certs = saveCertificate(plainList, savePath);
}
}
}
}
response.close();
httpClient.close(); //throw
return x509Certs;
} catch (Exception e) {
e.printStackTrace();
logger.error("下载平台证书返回结果:"+e);
}
return x509Certs;
}
4、结果
1、接口返回的内容是json串
{
"data": [{
"serial_no": "5157F09EFDC096DE15EBE81A47057A7232F1B8E1",
"effective_time ": "2018-06-08T10:34:56+08:00",
"expire_time ": "2018-12-08T10:34:56+08:00",
"encrypt_certificate": {
"algorithm": "AEAD_AES_256_GCM",
"nonce": "61f9c719728a",
"associated_data": "certificate",
"ciphertext": "sRvt… "
}
}]
}
2、通过接口下载的微信支付平台证书
山水有相逢,来日皆可期,谢谢阅读,我们再会
我手中的金箍棒,上能通天,下能探海