Heim  >  Artikel  >  Java  >  Detaillierte Erläuterung der WeChat-Zahlung in Java (1): API V3-Versionssignatur

Detaillierte Erläuterung der WeChat-Zahlung in Java (1): API V3-Versionssignatur

coldplay.xixi
coldplay.xixinach vorne
2020-10-27 17:20:212437Durchsuche

Java Basic Tutorial Die Kolumne stellt die WeChat-Zahlung in Java vor und implementiert die API V3-Versionssignatur.

Detaillierte Erläuterung der WeChat-Zahlung in Java (1): API V3-Versionssignatur

1. Einführung

Ich hatte in letzter Zeit Probleme mit der WeChat-Zahlung und das Zertifikat ist immer noch ziemlich nervig, daher ist es notwendig, einige Erfahrungen zu teilen, um Ihre Fallstricke bei der Entwicklung der WeChat-Zahlung zu reduzieren. Derzeit wurde die WeChat-Zahlungs-API auf die V3-Version weiterentwickelt und übernimmt den beliebten Restful-Stil.

Detaillierte Erläuterung der WeChat-Zahlung in Java (1): API V3-Versionssignatur

Heute werde ich die Schwierigkeiten der WeChat-Zahlung erläutern – Signature Obwohl es viele nützliche SDKs gibt, müssen Sie sie dennoch verstehen, wenn Sie die WeChat-Zahlung im Detail verstehen möchten.

2. API-Zertifikat

Um die Sicherheit sensibler Finanzdaten zu gewährleisten und sicherzustellen, dass die Finanztransaktionen in unserem Unternehmen narrensicher sind. Derzeit verwendet WeChat Pay den privaten Schlüssel, der im maßgeblichen CA-Zertifikat (API-Zertifikat) bereitgestellt wird, das von einem Dritten zur Signatur ausgestellt wurde. Sie können API-Zertifikate über die Händlerplattform einrichten und erhalten.

Detaillierte Erläuterung der WeChat-Zahlung in Java (1): API V3-Versionssignatur

Denken Sie daran, dass Sie beim ersten Einrichten zum Herunterladen aufgefordert werden. Weitere Informationen finden Sie in den Anweisungen.

Detaillierte Erläuterung der WeChat-Zahlung in Java (1): API V3-Versionssignatur说明

Suchen Sie nach der Einrichtung die zip压缩包解压,里面有很多文件,对于JAVA开发来说只需要关注apiclient_cert.p12这个证书文件就行了,它包含了公私钥,我们需要把它放在服务端并利用Java解析.p12-Datei, um den öffentlichen Schlüssel und den privaten Schlüssel zu erhalten.

Stellen Sie sicher, dass die Sicherheit des Zertifikats auf der Serverseite gewährleistet ist, was finanzielle Sicherheit beinhaltet.

Das API-Zertifikat analysieren

Der nächste Schritt besteht darin, das Zertifikat im Internet zu analysieren. Hier verwende ich eine „formellere“ Methode zum Parsen, nämlich java.security. KeyStore des zu analysierenden JDK-Sicherheitspakets. <code>java.security.KeyStore来解析。

微信支付Detaillierte Erläuterung der WeChat-Zahlung in Java (1): API V3-Versionssignatur使用了PKCS12算法,我们通过KeyStore来获取公私钥对的载体KeyPair以及证书序列号serialNumber,我封装了工具类:

import org.springframework.core.io.ClassPathResource;import java.security.KeyPair;import java.security.KeyStore;import java.security.PrivateKey;import java.security.PublicKey;import java.security.cert.X509Certificate;/**
 * KeyPairFactory
 *
 * @author dax
 * @since 13:41
 **/public class KeyPairFactory {    private KeyStore store;    private final Object lock = new Object();    /**
     * 获取公私钥.
     *
     * @param keyPath  the key path
     * @param keyAlias the key alias
     * @param keyPass  password
     * @return the key pair
     */
    public KeyPair createPKCS12(String keyPath, String keyAlias, String keyPass) {
        ClassPathResource resource = new ClassPathResource(keyPath);        char[] pem = keyPass.toCharArray();        try {            synchronized (lock) {                if (store == null) {                    synchronized (lock) {
                        store = KeyStore.getInstance("PKCS12");
                        store.load(resource.getInputStream(), pem);
                    }
                }
            }
            X509Certificate certificate = (X509Certificate) store.getCertificate(keyAlias);
            certificate.checkValidity();            // 证书的序列号 也有用
            String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase();            // 证书的 公钥
            PublicKey publicKey = certificate.getPublicKey();            // 证书的私钥
            PrivateKey storeKey = (PrivateKey) store.getKey(keyAlias, pem);    
            return new KeyPair(publicKey, storeKey);

        } catch (Exception e) {            throw new IllegalStateException("Cannot load keys from store: " + resource, e);
        }
    }
}复制代码

眼熟的可以看出是胖哥Spring Security教程中JWT用的公私钥提取方法的修改版本,你可以对比下不同之处。

这个方法中有三个参数,这里必须要说明一下:

  • keyPath  Detaillierte Erläuterung der WeChat-Zahlung in Java (1): API V3-Versionssignaturapiclient_cert.p12classpath路径,一般我们会放在resources路径下,当然你可以修改获取证书输入流的方式。
  • keyAlias 证书的别名,这个微信的文档是没有的,胖哥通过加载证书时进行DEBUG获取到该值固定为Tenpay Certificate
  • keyPass  证书密码,这个默认就是商户号,在其它配置中也需要使用就是mchid,就是你用超级管理员登录微信商户平台在个人资料中的一串数字。

3. V3签名

微信支付V3版本的签名是我们在调用具体的微信支付的API时在HTTP请求头中携带特定的编码串供微信支付服务器进行验证请求来源,确保请求是真实可信的。

签名格式

签名串的具体格式,一共五行一行也不能少,每一行以换行符n结束。

HTTP请求方法\n
URL\n
请求时间戳\n
请求随机串\n
请求报文主体\n复制代码
  • HTTP请求方法  你调用的微信支付API所要求的请求方法,比如APP支付为POST
  • URL  比如APP支付文档中为https://api.mch.weixin.qq.com/v3/pay/transactions/app,除去域名部分得到参与签名的URL。如果请求中有查询参数,URL末尾应附加有'?'和对应的查询字符串。这里为/v3/pay/transactions/app
  • 请求时间戳 服务器系统时间戳,保证服务器时间正确并利用System.currentTimeMillis() / 1000获取即可。
  • 请求随机串  找个工具类生成类似593BEC0C930BF1AFEB40B4A08C8FB242的字符串就行了。
  • 请求报文主体  如果是GET请求直接为空字符"" ;当请求方法为POSTPUT时,请使用真实发送JSON报文。图片上传API,请使用meta对应的JSON报文。

生成签名

然后我们使用商户私钥对按照上面格式的待签名串进行SHA256 with RSA签名,并对签名结果进行Base64编码得到签名值。对应的核心Java代码为:

/**
 * V3  SHA256withRSA 签名.
 *
 * @param method       请求方法  GET  POST PUT DELETE 等
 * @param canonicalUrl 例如  https://api.mch.weixin.qq.com/v3/pay/transactions/app?version=1 ——> /v3/pay/transactions/app?version=1
 * @param timestamp    当前时间戳   因为要配置到TOKEN 中所以 签名中的要跟TOKEN 保持一致
 * @param nonceStr     随机字符串  要和TOKEN中的保持一致
 * @param body         请求体 GET 为 "" POST 为JSON
 * @param keyPair      商户API 证书解析的密钥对  实际使用的是其中的私钥
 * @return the string
 */@SneakyThrowsString sign(String method, String canonicalUrl, long timestamp, String nonceStr, String body, KeyPair keyPair)  {
    String signatureStr = Stream.of(method, canonicalUrl, String.valueOf(timestamp), nonceStr, body)
            .collect(Collectors.joining("\n", "", "\n"));
    Signature sign = Signature.getInstance("SHA256withRSA");
    sign.initSign(keyPair.getPrivate());
    sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));    return Base64Utils.encodeToString(sign.sign());
}复制代码

4. 使用签名

签名生成后会同一些参数组成一个Token放置到对应HTTP请求的Authorization请求头中,格式为:

Authorization: WECHATPAY2-SHA256-RSA2048 {Token}复制代码

Token由以下五部分组成:

  • 发起请求的商户(包括直连商户、服务商或渠道商)的商户号mchid

  • 商户Detaillierte Erläuterung der WeChat-Zahlung in Java (1): API V3-Versionssignatur序列号serial_no,用于声明所使用的证书

  • 请求随机串nonce_str

  • 时间戳timestamp

  • 签名值signature

Token生成的核心代码:

/**
 * 生成Token.
 *
 * @param mchId 商户号
 * @param nonceStr   随机字符串 
 * @param timestamp  时间戳
 * @param serialNo   证书序列号
 * @param signature  签名
 * @return the string
 */String token(String mchId, String nonceStr, long timestamp, String serialNo, String signature) {    final String TOKEN_PATTERN = "mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\"";    // 生成token
    return String.format(TOKEN_PATTERN,
            wechatPayProperties.getMchId(),
            nonceStr, timestamp, serialNo, signature);
}复制代码

将生成的Token

Das WeChat-Zahlungs-API-Zertifikat verwendet den PKCS12-Algorithmus. Wir verwenden KeyStore, um den Träger des öffentlichen und privaten Schlüsselpaars KeyPair zu erhalten Zertifikatsseriennummer serialNumber, ich habe die Tool-Klasse gekapselt:

rrreee

Wenn es Ihnen bekannt vorkommt, können Sie sehen, dass es sich um eine modifizierte Version der von JWT im Frühjahr verwendeten Extraktionsmethode für öffentliche und private Schlüssel handelt Sicherheits-Tutorial. Sie können die Unterschiede vergleichen.

Es gibt drei Parameter in dieser Methode, die hier erklärt werden müssen:

3. V3-Signatur

Die Signatur der WeChat Pay V3-Version ist, wenn wir die spezifische WeChat Pay API A aufrufen Im HTTP-Anforderungsheader wird eine bestimmte Codierungszeichenfolge für den WeChat-Zahlungsserver übertragen, um die Quelle der Anforderung zu überprüfen und sicherzustellen, dass die Anforderung authentisch und vertrauenswürdig ist.

SignaturformatDas spezifische Format der Signaturzeichenfolge, insgesamt nicht weniger als fünf Zeilen, jede Zeile endet mit einem Zeilenumbruchzeichen n.
rrreee
  • HTTP-Anfragemethode Die von der von Ihnen aufgerufenen WeChat-Zahlungs-API benötigte Anfragemethode, beispielsweise APP-Zahlung, ist POST.
  • URL Das APP-Zahlungsdokument lautet beispielsweise https://api.mch.weixin.qq.com/v3/pay/transactions/app, Entfernen Sie den Teil des Domänennamens, um die URL zu erhalten, die an der Signatur beteiligt ist. Wenn die Anfrage Abfrageparameter enthält, sollten „?“ und die entsprechende Abfragezeichenfolge an das Ende der URL angehängt werden. Dies ist /v3/pay/transactions/app.
  • Zeitstempel anfordern Zeitstempel des Serversystems. Stellen Sie sicher, dass die Serverzeit korrekt ist, und verwenden Sie System.currentTimeMillis() / 1000, um ihn abzurufen.
  • Anfordern einer zufälligen Zeichenfolge Suchen Sie einfach eine Toolklasse, um eine Zeichenfolge ähnlich wie 593BEC0C930BF1AFEB40B4A08C8FB242 zu generieren.
  • Nachrichtentext anfordern Wenn es sich um eine GET-Anfrage handelt, ist sie direkt Nullzeichen"" code>; Wenn die Anforderungsmethode POST oder PUT ist, verwenden Sie bitte die JSON-Nachricht, die wirklich gesendet wird. Für die Bild-Upload-API verwenden Sie bitte die JSON-Nachricht, die meta entspricht.

Signatur generieren

Dann verwenden wir den privaten Schlüssel des Händlers, um SHA256 mit RSA-Signatur auf der zu signierenden Zeichenfolge durchzuführen im oben genannten Format und führen Sie eine Base64-Codierung für das Signaturergebnis durch, um den Signaturwert zu erhalten. Der entsprechende Kern-Java-Code lautet: 🎜rrreee

4 Verwenden Sie die Signatur 🎜🎜Nachdem die Signatur generiert wurde, wird sie mit einigen Parametern kombiniert, um ein Token zu bilden. code> und fügen Sie es in den <code> ein, der dem HTTP-Anforderungsheader code>Authorization entspricht. Das Format lautet: 🎜rrreee🎜Token besteht aus den folgenden fünf Teilen: 🎜
  • 🎜Der Händler, der die Anfrage initiiert hat (einschließlich direkt verbundener Händler, Dienstleistungshändler oder Kanalanbieter) Händlernummer mchid🎜
  • 🎜Seriennummer des Händler-API-Zertifikats serial_no, wird verwendet, um das verwendete Zertifikat zu deklarieren🎜
  • 🎜Zufällige Zeichenfolge anfordern nonce_str🎜
  • 🎜Zeitstempel timestamp code>🎜
  • 🎜Signaturwert signatur 🎜
  • 🎜TokenDer generierte Kerncode: 🎜rrreee🎜Setzen Sie den generierten Code ein Token in den Anforderungsheader gemäß dem oben genannten Format einfügen. Die Verwendung von Signaturen kann abgeschlossen werden. 🎜🎜5. Zusammenfassung🎜🎜In diesem Artikel haben wir eine vollständige Analyse der schwierigen Signaturen und der Verwendung von Signaturen in der WeChat Pay V3-Version durchgeführt und auch die Analyse von API-Zertifikaten erläutert, die Ihnen bei der Lösung einiger Probleme helfen kann spezifische Probleme bei der Zahlungsentwicklung. 🎜🎜🎜Verwandte kostenlose Lernempfehlungen: 🎜Java-Grundlagen-Tutorial🎜🎜🎜Einführung in den verwandten Artikel: 🎜So implementieren Sie die Miniprogramm-Zahlungsfunktion🎜🎜🎜🎜

    Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der WeChat-Zahlung in Java (1): API V3-Versionssignatur. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Dieser Artikel ist reproduziert unter:juejin.im. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen