必须从微信商户平台「API安全」页手动下载证书,且每次下载生成新证书对;私钥需用OpenSSL解密后加载;签名串须严格按顺序拼接并以换行结尾。

微信支付 APIv3 证书怎么下载才有效
下载的证书不能直接用,必须从微信商户平台「API安全」页手动触发下载,且每次下载会生成新证书对(apiclient_cert.pem、apiclient_key.pem、apiclient_cert.p12)。用旧证书或从其他渠道复制的私钥必然报 java.security.InvalidKeyException: Illegal key size 或签名失败。
- 必须登录【微信商户平台】→【账户中心】→【API安全】→ 点击「APIv3密钥」旁的「下载证书」按钮,不能右键另存为页面上的链接
-
apiclient_key.pem是 PKCS#8 格式私钥,Java 里直接用PKCS8EncodedKeySpec加载;若误用 PKCS#1(以-----BEGIN RSA PRIVATE KEY-----开头),KeyFactory.getInstance("RSA")会抛InvalidKeySpecException - 证书链要完整:微信返回的
apiclient_cert.pem包含商户证书 + 平台证书(中间 CA),不能只截取其中一段
Java 加载 apiclient_key.pem 总是抛异常
根本原因是 Java 默认不支持密码保护的 PEM 私钥,而微信导出的 apiclient_key.pem 默认带密码(即你在下载时设置的「APIv3密钥」),但 JDK 的 PemReader 或 KeyFactory 不处理 PEM 密码解密逻辑。
- 最稳妥做法:用 OpenSSL 命令提前解密私钥:
openssl pkcs8 -in apiclient_key.pem -out apiclient_key_decrypted.pem -nodes,输入下载时设的 APIv3 密钥 - 代码中加载时,必须用
PEMParser(Bouncy Castle)配合JcaPEMKeyConverter,不能用Files.readAllBytes+Base64.getDecoder().decode()粗暴解析 - 别把
apiclient_cert.p12当成万能替代——它虽带密码,但 Java 加载需指定 storeType ="PKCS12",且 alias 固定为"apiclient",漏掉任一参数都会KeyStoreException
调用 v3/pay/transactions/native 签名老是 401
401 不代表密钥错,大概率是签名串构造不一致。微信 APIv3 要求按固定顺序拼接 HTTP 方法、路径、查询参数、请求体哈希、时间戳、随机字符串,缺一不可,且大小写、空格、换行都敏感。
- 请求体必须先做
SHA256哈希(空体也要哈希成e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855),不能直接传原文 - 时间戳必须用秒级 Unix 时间(
System.currentTimeMillis() / 1000),不是毫秒,也不是Instant.now().getEpochSecond()(这个没问题,但有人误用toEpochMilli) - 签名串最后一行必须是空行(\n),少这个换行,微信服务端校验直接失败,错误信息只回
{"code":"INVALID_SIGNATURE","message":"签名验证失败"}
Spring Boot 项目里证书文件放哪、怎么读最稳
别放 src/main/resources 下用 ClassPathResource 读——本地 OK,打成 jar 后常因 ClassLoader 差异读不到流;也别硬编码绝对路径,部署到 Docker 容器就挂。
立即学习“Java免费学习笔记(深入)”;
- 推荐放项目根目录外的配置目录,比如
/etc/wechat/certs/,用Paths.get("/etc/wechat/certs/apiclient_cert.pem")直接读取 - 如果必须放 classpath,用
ResourceUtils.getFile("classpath:certs/apiclient_cert.pem"),但得确保打包时没被 Maven Resource 插件过滤掉(检查pom.xml中<resources>是否 exclude 了 pem) - 证书内容建议启动时一次性读入内存并缓存,别每次请求都
Files.readAllBytes——IO 开销大,且高并发下可能触发文件句柄耗尽











