使用sdk下载的平台证书加密敏感信息后接口报错 平台私钥解密失败
Closed this issue · 7 comments
错误描述
首先,sdk提供了下载平台证书的服务,却没有返回证书的序列号,加密敏感信息时需要用到证书序列号。没办法只能手工调用获取平台证书的接口,对返回信息进行解密后拿到平台证书公钥对敏感信息解密并在请求头中设置了对应的证书序列号。提交接口时返回 平台私钥解密失败。
重现bug的步骤
1.手动调用获取平台证书接口获取平台证书
2.获取到平台证书序列号
3.将响应密文解密获取到平台证书
4.使用平台证书公钥对敏感信息(银行卡号)加密并拼接到请求地址(获取对私银行卡号开户银行API)
5.在请求头中附加加密使用的平台证书序列号
6.调用接口 (获取对私银行卡号开户银行API)
7.接口返回 平台私钥解密失败
预期行为
预期微信后台对加密后的数据正常解密并返回相应的开户行
导致错误的代码片段
// 获取平台证书序列号及证书
HttpClient httpClient = new DefaultHttpClientBuilder().config(entCertificateConfig).build();
String url = "https://api.mch.weixin.qq.com/v3/certificates";
com.wechat.pay.java.core.http.HttpHeaders headers = new com.wechat.pay.java.core.http.HttpHeaders();
headers.addHeader(Constant.ACCEPT, " */*");
headers.addHeader(Constant.CONTENT_TYPE, MediaType.APPLICATION_JSON.getValue());
HttpRequest httpRequest = new HttpRequest.Builder()
.headers(headers)
.httpMethod(HttpMethod.GET)
.url(url)
.build();
HttpResponse<DownloadCertificateResponse> httpResponse =
httpClient.execute(httpRequest, DownloadCertificateResponse.class);
Data data = httpResponse.getServiceResponse().getData().get(0);
wxPlatCertificateSer = data.getSerialNo();
EncryptCertificate encryptCertificate = data.getEncryptCertificate();
AeadCipher aeadCipher = new AeadAesCipher(entWxPayConfig.getApiV3Key().getBytes());
String decryptCertificate = aeadCipher.decrypt(
encryptCertificate.getAssociatedData().getBytes(StandardCharsets.UTF_8),
encryptCertificate.getNonce().getBytes(StandardCharsets.UTF_8),
Base64.getDecoder().decode(encryptCertificate.getCiphertext()));
wxPlatCertificate = PemUtil.loadX509FromString(decryptCertificate);
// 加密敏感数据
public static String rsaEncryptOAEP(String message, X509Certificate certificate)
throws IllegalBlockSizeException, IOException {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());
byte[] data = message.getBytes(StandardCharsets.UTF_8);
byte[] cipherdata = cipher.doFinal(data);
return Base64.getEncoder().encodeToString(cipherdata);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
} catch (InvalidKeyException e) {
throw new IllegalArgumentException("无效的证书", e);
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
}
}
操作系统
windows10
Java 版本
java 8
wechatpay-java 版本
0.2.7
其他信息
No response
可以从 Config.createEncryptor()
获得一个加密器,加密器也有方法获得证书序列号。
目前是在常见问题中有提到,我提前到加解密的章节中补充说明下。
可以从
Config.createEncryptor()
获得一个加密器,加密器也有方法获得证书序列号。目前是在常见问题中有提到,我提前到加解密的章节中补充说明下。
@xy-peng 我换成了你说的方法,接口仍然返回 平台私钥解密失败。
问题的根源是微信端无法解密我加密的数据
public DepositBankResponse searchBanksByAccount(String accountNumber) {
HttpClient httpClient = new DefaultHttpClientBuilder().config(entCertificateConfig).build();
PrivacyEncryptor encryptor = entCertificateConfig.createEncryptor();
String encryptNumber = encryptor.encrypt(accountNumber);
String url = "https://api.mch.weixin.qq.com/v3/capital/capitallhh/banks/search-banks-by-bank-account?account_number=" + encryptNumber;
com.wechat.pay.java.core.http.HttpHeaders headers = new com.wechat.pay.java.core.http.HttpHeaders();
headers.addHeader(Constant.ACCEPT, com.wechat.pay.java.core.http.MediaType.APPLICATION_JSON.getValue());
headers.addHeader("Wechatpay-Serial", encryptor.getWechatpaySerial());
HttpRequest httpRequest = new HttpRequest.Builder()
.headers(headers)
.httpMethod(HttpMethod.GET)
.url(url)
.build();
HttpResponse<DepositBankResponse> response;
try {
response = httpClient.execute(httpRequest, DepositBankResponse.class);
} catch (com.wechat.pay.java.core.exception.ServiceException e) {
LOGGER.error("根据银行账户查询开户行失败:" + e.getResponseBody());
throw new ServiceException(e.getErrorMessage());
}
return response.getServiceResponse();
}
麻烦提供下商户号或者应答的 request-id ?
麻烦提供下商户号或者应答的 request-id ?
@xy-peng 1652310197
Request-ID -> 08A29798A90610BF0618A6E1EE5C20D81B289FFD01-269546542
PrivacyEncryptor encryptor = entCertificateConfig.createEncryptor();
String encryptNumber = encryptor.encrypt(accountNumber);
String url = "https://api.mch.weixin.qq.com/v3/capital/capitallhh/banks/search-banks-by-bank-account?account_number=" + encryptNumber;
你试试对 encryptNumber
做一次 urlencode,base64编码不是 url safe 的
PrivacyEncryptor encryptor = entCertificateConfig.createEncryptor(); String encryptNumber = encryptor.encrypt(accountNumber); String url = "https://api.mch.weixin.qq.com/v3/capital/capitallhh/banks/search-banks-by-bank-account?account_number=" + encryptNumber;你试试对
encryptNumber
做一次 urlencode,base64编码不是 url safe 的
可以了,感谢。但是在官方文档中的请求示例并没有做urlencode,也没有进行说明,容易造成误导,希望可以改进。
文档的问题已经交由负责相关 API 的同事了