1.前言最近一直在折腾微信支付,证书比较烦人,所以有必要分享一些经验,减少大家开发微信支付的坑。目前微信支付API已经开发到V3版本,采用时下流行的Restful风格。微信支付V2和V3的区别今天给大家分享一下微信支付的难点——签名。虽然有很多好用的SDK,但如果你想深入了解微信支付,还是有帮助的。2、API证书用于保证敏感金融数据的安全,保证我们业务中的金融交易万无一失。目前微信支付使用第三方颁发的权威CA证书(API证书)中提供的私钥进行签名。通过商户平台,您可以设置和获取API证书。记住第一次设置API证书会提示下载,以后就不能下载了,具体请看说明。API证书描述设置好后,找到zip压缩包解压。里面有很多文件。JAVA开发只需要关注证书文件apiclient_cert.p12即可。它包含公钥和私钥。我们需要把它放在服务器上,用Java解析.p12文件来获取公钥和私钥。保证服务器端证书的安全很重要,这涉及到资金安全。解析API证书下一步是解析证书。网上有很多解析证书的方法。这里我用比较“正规”的方法来解析,使用JDK安全包的java.security.KeyStore来解析。微信支付API证书使用PKCS12算法。我们使用KeyStore获取公私钥对的载体KeyPair和证书序列号serialNumber。我封装了工具类(序列号你自己处理):security.cert.X509Certificate;/***KeyPairFactory**@authordax*@since13:41**/publicclassKeyPairFactory{privateKeyStorestore;privatefinalObjectlock=newObject();/***获取公钥和私钥。**@paramkeyPaththekeypath*@paramkeyAliasthekeyalias*@paramkeyPasspassword*@returnthekeypair*/publicKeyPaircreatePKCS12(StringkeyPath,StringkeyAlias,StringkeyPass){ClassPathResourceresource=newClassPathResource[keyPath];pem=keyPass.toCharArray();try{synchronized(lock){if(store==null){synchronized(lock){store=KeyStore.getInstance("PKCS12");store.load(resource.getInputStream(),pem);}}}X509Certificatecertificate=(X509Certificate)store.getCertificate(keyAlias);certificate.checkValidity();//证书的序列号也是有用的StringserialNumber=certificate.getSerialNumber().toString(16).toUpperCase();//证书公钥PublicKeypublicKey=certificate.getPublicKey();//证书私钥PrivateKeystoreKey=(PrivateKey)store.getKey(keyAlias,pem);returnnewKeyPair(publicKey,storeKey);}catch(Exceptione){thrownewIllegalStateException("Cannotloadkeysfromstore:"+resource,e);}}}可以看到是修改了使用的公私钥提取方式通过胖哥SpringSecurity教程中的JWT版本,大家可以对比一下差异。该方法中有3个参数,这里必须说明一下:keyPathAPI证书apiclient_cert.p12的classpath路径。一般我们会放在resources路径下。当然,你可以修改输入流获取证书的方式。keyAlias证书的别名,这个微信文档没有,胖哥在加载证书的时候通过DEBUG获取到这个值,固定为财付通证书。keyPass证书密码,默认为商户ID,其他配置也需要。就是mchid,是你以超级管理员身份登录微商平台时,你的个人资料里的一串数字。3、V3签名微信支付的V3签名是当我们调用具体的微信支付接口时,在HTTP请求头中携带一个特定的编码字符串,供微信支付服务器验证请求的来源,保证请求的真实性.签名格式签名字符串的具体格式,一共五行,一行不能少,每行以换行符\n结尾。HTTP请求方式\nURL\n请求时间戳\n请求随机字符串\n请求消息体\nHTTP请求方式你调用的微信支付接口要求的请求方式,比如APP支付是POST。例如APP支付文档中的URL为https://api.mch.weixin.qq.com/v3/pay/transactions/app,参与签名的URL是去掉域名得到的。如果请求中有查询参数,URL应该附加'?'和相应的查询字符串。这是/v3/pay/transactions/app。请求时间戳服务器系统时间戳,确保服务器时间正确,使用System.currentTimeMillis()/1000获取。请求随机字符串随便找个工具生成类似593BEC0C930BF1AFEB40B4A08C8FB242的字符串。如果请求报文体是GET请求,则直接为空字符"";当请求方式为POST或PUT时,请使用真实发送的JSON报文。图片上传接口请使用meta对应的json消息。生成签名然后使用商户私钥按照上述格式对待签名字符串进行SHA256加RSA签名,对签名结果进行base64编码得到签名值。对应的Java核心代码为:/***V3SHA256withRSA签名。**@parammethod请求方法GETPOSTPUTDELETE等*@paramcanonicalUrl例如https://api.mch.weixin.qq.com/v3/pay/transactions/app?version=1——>/v3/pay/transactions/app?version=1*@paramtimestampTOKEN中必须配置当前时间戳,所以签名必须与TOKEN一致*@paramnonceStr随机字符串必须与TOKEN*@parambodyrequestbodyGETis""POSTisJSON*@paramkeyPair商户API证书解析出的密钥对实际使用私钥*@returnthestring*/@SneakyThrowsStringsign(Stringmethod,StringcanonicalUrl,longtimestamp,StringnonceStr,Stringbody,KeyPairkeyPair){StringsignatureStr=Stream.of(method,canonicalUrl,String.valueOf(timestamp),nonceStr,body).collect(Collectors.joining("\n","","\n"));Signaturesign=Signature.getInstance("SHA256withRSA");sign.initSign(keyPair.getPrivate());sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));returnBase64Utils.encodeToString(sign.sign());}4.使用signature签名后结合一些参数,形成一个Token,放在相应HTTP请求的Authorization请求头中。格式为:Authorization:WECHATPAY2-SHA256-RSA2048{Token}Token由以下五部分组成:发起请求的商户(包括直连商户、服务商或渠道商)的商户IDmchid商户API证书序列号serial_no,用于声明使用的随机字符串对于证书申请nonce_strtimestamptimestampsignaturevaluesignatureToken生成核心代码:/***GenerateToken.**@parammchId商户号*@paramnonceStr随机串*@paramtimestamptimestamp*@paramserialNocertificate序列号*@paramsignaturesignature*@returnthestring*/Stringtoken(StringmchId,StringnonceStr,longtimestamp,StringserialNo,Stringsignature){finalStringTOKEN_PATTERN="mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\"";//生成tokenreturnString.format(TOKEN_PATTERN,wechatPayProperties.getMchId(),nonceStr,timestamp,serialNo,signature);}把生成的Token按照上面的格式放到请求头中你可以完成签名的使用5.总结在这篇文章中,我们对微信支付V3版本签名的难点和签名的使用进行了完整的分析。同时我们也讲解了API证书的解析。相信可以帮助大家解决支付开发中的一些问题。具体问题。本文转载自微信公众号“码农小胖哥”,可通过以下二维码关注。转载本文请联系码农小胖公众号。
