### HTTP Request Method Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-sheng-cheng.md The first line of the signature string is the HTTP request method (e.g., GET, POST, PUT). ```http GET ``` -------------------------------- ### .Net HttpClientHandler for Signature Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-sheng-cheng.md An example of a .Net HttpClientHandler that automatically adds the WeChat Pay V3 Authorization header with a generated signature to outgoing requests. ```csharp using System; using System.IO; using System.Net.Http; using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; namespace HttpHandlerDemo { // 使用方法 // HttpClient client = new HttpClient(new HttpHandler("{商户号}", "{商户证书序列号}")); // ... // var response = client.GetAsync("https://api.mch.weixin.qq.com/v3/certificates"); public class HttpHandler : DelegatingHandler { private readonly string merchantId; private readonly string serialNo; public HttpHandler(string merchantId, string merchantSerialNo) { InnerHandler = new HttpClientHandler(); this.merchantId = merchantId; this.serialNo = merchantSerialNo; } protected async override Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { var auth = await BuildAuthAsync(request); string value = $"WECHATPAY2-SHA256-RSA2048 {auth}"; request.Headers.Add("Authorization", value); return await base.SendAsync(request, cancellationToken); } protected async Task BuildAuthAsync(HttpRequestMessage request) { string method = request.Method.ToString(); string body = ""; if (method == "POST" || method == "PUT" || method == "PATCH") { var content = request.Content; body = await content.ReadAsStringAsync(); } string uri = request.RequestUri.PathAndQuery; var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); string nonce = Path.GetRandomFileName(); string message = $"{method}\n{uri}\n{timestamp}\n{nonce}\n{body}\n"; string signature = Sign(message); return $"mchid=\"{merchantId}\",nonce_str=\"{nonce}\",timestamp=\"{timestamp}\",serial_no=\"{serialNo}\",signature=\"{signature}\""; } protected string Sign(string message) { // NOTE: 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY----- // 亦不包括结尾的-----END PRIVATE KEY----- string privateKey = "{你的私钥}"; byte[] keyData = Convert.FromBase64String(privateKey); var rsa = RSA.Create(); rsa.ImportPkcs8PrivateKey(keyData, out _); byte[] data = System.Text.Encoding.UTF8.GetBytes(message); return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)); } } } ``` -------------------------------- ### Example Authorization Header for WeChat Pay API v3 Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-sheng-cheng.md This is an example of the HTTP Authorization header required for WeChat Pay API v3 requests. It includes the authentication type, merchant ID, nonce string, signature, timestamp, and certificate serial number. Ensure all components are correctly formatted and included. ```http Authorization: WECHATPAY2-SHA256-RSA2048 mchid="1900009191",nonce_str="593BEC0C930BF1AFEB40B4A08C8FB242",signature="uOVRnA4qG/MNnYzdQxJanN+zU+lTgIcnU9BxGw5dKjK+VdEUz2FeIoC+D5sB/LN+nGzX3hfZg6r5wT1pl2ZobmIc6p0ldN7J6yDgUzbX8Uk3sD4a4eZVPTBvqNDoUqcYMlZ9uuDdCvNv4TM3c1WzsXUrExwVkI1XO5jCNbgDJ25nkT/c1gIFvqoogl7MdSFGc4W4xZsqCItnqbypR3RuGIlR9h9vlRsy7zJR9PBI83X8alLDIfR1ukt1P7tMnmogZ0cuDY8cZsd8ZlCgLadmvej58SLsIkVxFJ8XyUgx9FmutKSYTmYtWBZ0+tNvfGmbXU7cob8H/4nLBiCwIUFluw==",timestamp="1554208460",serial_no="1DDE55AD98ED71D6EDD4A4A16996DE7B47773A8C" ``` -------------------------------- ### Java Signature Generation Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-sheng-cheng.md Example of how to generate a WeChat Pay V3 signature in Java. This snippet includes methods for building the message to be signed and performing the RSA-SHA256 signing. ```Java import okhttp3.HttpUrl; import java.security.Signature; import java.util.Base64; // Authorization: // GET - getToken("GET", httpurl, "") // POST - getToken("POST", httpurl, json) String schema = "WECHATPAY2-SHA256-RSA2048"; HttpUrl httpurl = HttpUrl.parse(url); String getToken(String method, HttpUrl url, String body) { String nonceStr = "your nonce string"; long timestamp = System.currentTimeMillis() / 1000; String message = buildMessage(method, url, timestamp, nonceStr, body); String signature = sign(message.getBytes("utf-8")); return "mchid=\"" + yourMerchantId + "\"," + "nonce_str=\"" + nonceStr + "\"," + "timestamp=\"" + timestamp + "\"," + "serial_no=\"" + yourCertificateSerialNo + "\"," + "signature=\"" + signature + "\""; } String sign(byte[] message) { Signature sign = Signature.getInstance("SHA256withRSA"); sign.initSign(yourPrivateKey); sign.update(message); return Base64.getEncoder().encodeToString(sign.sign()); } String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) { String canonicalUrl = url.encodedPath(); if (url.encodedQuery() != null) { canonicalUrl += "?" + url.encodedQuery(); } return method + "\n" + canonicalUrl + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n"; } ``` -------------------------------- ### Making a Request with Authorization Header using curl Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-sheng-cheng.md This command demonstrates how to make a GET request to the WeChat Pay API v3 certificates endpoint, including the Authorization header with all necessary signature components. Replace placeholders with your actual values. ```bash $ curl https://api.mch.weixin.qq.com/v3/certificates -H 'Authorization: WECHATPAY2-SHA256-RSA2048 mchid="1900009191",nonce_str="593BEC0C930BF1AFEB40B4A08C8FB242",signature="uOVRnA4qG/MNnYzdQxJanN+zU+lTgIcnU9BxGw5dKjK+VdEUz2FeIoC+D5sB/LN+nGzX3hfZg6r5wT1pl2ZobmIc6p0ldN7J6yDgUzbX8Uk3sD4a4eZVPTBvqNDoUqcYMlZ9uuDdCvNv4TM3c1WzsXUrExwVkI1XO5jCNbgDJ25nkT/c1gIFvqoogl7MdSFGc4W4xZsqCItnqbypR3RuGIlR9h9vlRsy7zJR9PBI83X8alLDIfR1ukt1P7tMnmogZ0cuDY8cZsd8ZlCgLadmvej58SLsIkVxFJ8XyUgx9FmutKSYTmYtWBZ0+tNvfGmbXU7cob8H/4nLBiCwIUFluw==",timestamp="1554208460",serial_no="1DDE55AD98ED71D6EDD4A4A16996DE7B47773A8C"' ``` -------------------------------- ### Go RSA OAEP Decryption Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/min-gan-xin-xi-jia-mi.md Decrypts ciphertext using RSA OAEP padding with SHA-1. This example decodes Base64 ciphertext, performs decryption using a provided private key, and prints the plaintext. Error handling for decryption is included. ```go cipherdata, _ := base64.StdEncoding.DecodeString(ciphertext) rng := rand.Reader plaintext, err := DecryptOAEP(sha1.New(), rng, rsaPrivateKey, cipherdata, nil) if err != nil { fmt.Fprintf(os.Stderr, "Error from decryption: %s\n", err) return } fmt.Printf("Plaintext: %s\n", string(plaintext)) ``` -------------------------------- ### Get Platform Certificates List Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/jie-kou-wen-dang/ping-tai-zheng-shu.md Retrieves a list of available platform certificates for the merchant. This API is essential for implementing a smooth certificate replacement process and ensuring ongoing transaction security. It requires signature authentication and does not use query or path parameters. ```APIDOC ## GET /v3/certificates ### Description Retrieves a list of available platform certificates for the merchant. This API is essential for implementing a smooth certificate replacement process and ensuring ongoing transaction security. It requires signature authentication and does not use query or path parameters. ### Method GET ### Endpoint https://api.mch.weixin.qq.com/v3/certificates ### Parameters #### Headers - **Authorization** (string) - Required - Signature authentication information. - **Accept** (string) - Required - Should be set to "application/json". - **User-Agent** (string) - Optional - User agent string. ### Response #### Success Response (200) - **data** (array) - List of platform certificates. - **serial_no** (string) - The serial number of the certificate. - **effective_time** (string) - The time when the certificate becomes effective (ISO 8601 format). - **expire_time** (string) - The time when the certificate expires (ISO 8601 format). - **encrypt_certificate** (object) - Contains details for decrypting the certificate. - **algorithm** (string) - The encryption algorithm used (e.g., "AEAD_AES_256_GCM"). - **nonce** (string) - The nonce used for encryption. - **associated_data** (string) - Associated data used during encryption. - **ciphertext** (string) - The encrypted certificate content. #### Response Example ```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… " } }, { "serial_no": "50062CE505775F070CAB06E697F1BBD1AD4F4D87", "effective_time ": "2018-12-07T10:34:56+08:00", "expire_time ": "2020-12-07T10:34:56+08:00", "encrypt_certificate": { "algorithm": "AEAD_AES_256_GCM", "nonce": "35f9c719727b", "associated_data": "certificate", "ciphertext": "aBvt… " } } ] } ``` ### Notes - This interface has no query or path parameters; only necessary header information, such as signature authentication, is required. - WeChat Pay will add new certificates to the platform certificate list 24 hours before they become active. - The API rate limit is 1000 requests per second per merchant ID. - For initial certificate downloads, consider using the [Certificate Download Tool](https://github.com/wechatpay-apiv3/CertificateDownloader). - When implementing platform signature verification, ensure your program regularly updates platform certificates and calls this interface at intervals less than 12 hours. Use the latest platform certificate (the one with the later activation time) when encrypting sensitive information. ``` -------------------------------- ### Request Body for Signature Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-sheng-cheng.md The fifth line is the request body. For GET requests, this is empty. For POST/PUT, use the actual JSON payload. For image uploads, use the 'meta' JSON. ```text ``` -------------------------------- ### Get Platform Certificates List Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/jie-kou-wen-dang/ping-tai-zheng-shu.md Use this endpoint to retrieve a list of available platform certificates. This is essential for implementing smooth certificate renewals and ensuring uninterrupted transactions. The response contains certificate serial numbers, validity periods, and encrypted certificate details. ```javascript { "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… " } }, { "serial_no": "50062CE505775F070CAB06E697F1BBD1AD4F4D87", "effective_time ": "2018-12-07T10:34:56+08:00", "expire_time ": "2020-12-07T10:34:56+08:00", "encrypt_certificate": { "algorithm": "AEAD_AES_256_GCM", "nonce": "35f9c719727b", "associated_data": "certificate", "ciphertext": "aBvt… " } } ] } ``` -------------------------------- ### PHP: 从文件加载私钥 Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/qian-ming-xiang-guan.md 此函数从指定的PEM编码私钥文件路径读取内容,并使用openssl_get_privatekey函数获取私钥资源。适用于PHP环境。 ```php /** * Read private key from file * * @param string $filepath PEM encoded private key file path * * @return resource|bool Private key resource identifier on success, or FALSE on error */ public static function getPrivateKey($filepath) { return openssl_get_privatekey(file_get_contents($filepath)); } ``` -------------------------------- ### Generate Signature Value using Command Line Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-sheng-cheng.md This snippet demonstrates how to generate a signature value using command-line tools. It pipes the request details to openssl for SHA256 with RSA signing and then Base64 encoding. Ensure you have your private key (apiclient_key.pem) available. ```bash $ echo -n -e \ "GET\n/v3/certificates\n1554208460\n593BEC0C930BF1AFEB40B4A08C8FB242\n\n" \ | openssl dgst -sha256 -sign apiclient_key.pem \ | openssl base64 -A uOVRnA4qG/MNnYzdQxJanN+zU+lTgIcnU9BxGw5dKjK+VdEUz2FeIoC+D5sB/LN+nGzX3hfZg6r5wT1pl2ZobmIc6p0ldN7J6yDgUzbX8Uk3sD4a4eZVPTBvqNDoUqcYMlZ9uuDdCvNv4TM3c1WzsXUrExwVkI1XO5jCNbgDJ25nkT/c1gIFvqoogl7MdSFGc4W4xZsqCItnqbypR3RuGIlR9h9vlRsy7zJR9PBI83X8alLDIfR1ukt1P7tMnmogZ0cuDY8cZsd8ZlCgLadmvej58SLsIkVxFJ8XyUgx9FmutKSYTmYtWBZ0+tNvfGmbXU7cob8H/4nLBiCwIUFluw== ``` -------------------------------- ### Verifying Merchant Private Key and Certificate Match Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/qian-ming-xiang-guan.md Use these OpenSSL commands to verify that your merchant private key and certificate are a matching pair by comparing their moduli. This is crucial for ensuring correct signature generation. ```bash $ openssl x509 -noout -modulus -in 1900009191_20180326_cert.pem Modulus=C6D43C87B991... ``` ```bash $ openssl rsa -noout -modulus -in 1900009191_20180326_key.pem Modulus=C6D43C87B991... ``` -------------------------------- ### 导出微信支付平台公钥 Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-yan-zheng.md 使用openssl命令从微信支付平台证书文件中导出公钥,用于后续的签名验证。 ```bash $ openssl x509 -in 1900009191_wxp_cert.pem -pubkey -noout > 1900009191_wxp_pub.pem $ cat 1900009191_wxp_pub.pem -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4zej1cqugGQtVSY2Ah8R MCKcr2UpZ8Npo+5Ja9xpFPYkWHaF1Gjrn3d5kcwAFuHHcfdc3yxDYx6+9grvJnCA 2zQzWjzVRa3BJ5LTMj6yqvhEmtvjO9D1xbFTA2m3kyjxlaIar/RYHZSslT4VmjIa tW9KJCDKkwpM6x/RIWL8wwfFwgz2q3Zcrff1y72nB8p8P12ndH7GSLoY6d2Tv0OB 2+We2Kyy2+QzfGXOmLp7UK/pFQjJjzhSf9jxaWJXYKIBxpGlddbRZj9PqvFPTiep 8rvfKGNZF9Q6QaMYTpTp/uKQ3YvpDlyeQlYe4rRFauH3mOE6j56QlYQWivknDX9V rwIDAQAB -----END PUBLIC KEY----- ``` -------------------------------- ### .Net: 加载PKCS12文件并签名 Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/qian-ming-xiang-guan.md 此方法加载指定的PKCS12文件,提取其私钥,并使用该私钥对组合后的字符串进行SHA256签名。需要提供P12文件路径和商户号(作为密码)。 ```csharp private string Sign(string addr, string method, int time, string nonce, string jsonstr) { var stem = new Uri(addr).PathAndQuery; var str = Utils.JoinWith("\n", method, stem, time, nonce, jsonstr, null); using (var x509 = new X509Certificate2(Utils.ReadBytesIfExist(@"P12文件路径"), "商户号")) { using (var rsa = (RSACryptoServiceProvider)x509.PrivateKey) { return Convert.ToBase64String(rsa.SignData(Encoding.UTF8.GetBytes(str), "SHA256")); } } } ``` -------------------------------- ### Verify Platform Certificate with Trust Chain Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/zheng-shu-xiang-guan.md Use openssl to verify a downloaded platform certificate against a trusted certificate chain file. ```bash openssl verify -verbose -CAfile ./CertTrustChain.pem ./WeChatPayPlatform.pem ``` -------------------------------- ### Date and Time Formats (ISO 8601) Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/wei-xin-zhi-fu-api-v3-jie-kou-gui-fan.md WeChat Pay API v3 uses ISO 8601 format for all date objects. Examples show variations with and without milliseconds and timezone offsets. ```text yyyy-MM-DDTHH:mm:ss.SSSZ yyyy-MM-DDTHH:mm:ssZ yyyy-MM-DDTHH:mm:ss.SSS+08:00 yyyy-MM-DDTHH:mm:ss+08:00 ``` -------------------------------- ### 使用openssl验证签名 Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-yan-zheng.md 使用openssl dgst命令,结合公钥、签名文件和验签名串,执行SHA256 with RSA签名验证。 ```bash $ openssl dgst -sha256 -verify 1900009191_wxp_pub.pem -signature signature.txt << EOF 1554209980 c5ac7061fccab6bf3e254dcf98995b8c {"data":[{"serial_no":"5157F09EFDC096DE15EBE81A47057A7232F1B8E1","effective_time":"2018-03-26T11:39:50+08:00","expire_time":"2023-03-25T11:39:50+08:00","encrypt_certificate":{"algorithm":"AEAD_AES_256_GCM","nonce":"d215b0511e9c","associated_data":"certificate","ciphertext":"..."}}]} EOF Verified OK ``` -------------------------------- ### Java: 加载RSA私钥文件 Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/qian-ming-xiang-guan.md 此代码从指定文件路径读取私钥内容,去除头部和尾部标记及空格,然后将其解码为RSA私钥对象。适用于Java环境。 ```java /** * 获取私钥。 * * @param filename 私钥文件路径 (required) * @return 私钥对象 */ public static PrivateKey getPrivateKey(String filename) throws IOException { String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8"); try { String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "") .replace("-----END PRIVATE KEY-----", "") .replaceAll("\\s+", ""); KeyFactory kf = KeyFactory.getInstance("RSA"); return kf.generatePrivate( new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey))); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("当前Java环境不支持RSA", e); } catch (InvalidKeySpecException e) { throw new RuntimeException("无效的密钥格式"); } } ``` -------------------------------- ### .Net: 使用PKCS8格式私钥签名 Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/qian-ming-xiang-guan.md 此方法直接使用Base64编码的PKCS8格式私钥字符串对消息进行SHA256签名。适用于.Net Framework 4.6或.Net Core 2.0。 ```csharp protected string sign(string message) { // 需去除私钥文件中的-----BEGIN/END PRIVATE KEY----- string privateKey = "MIIEvgIBADANBgkqhkiG...30HBe+GD1tntZgf6I1Y0ZpHZ"; byte[] keyData = Convert.FromBase64String(privateKey); using (CngKey cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob)) using (RSACng rsa = new RSACng(cngKey)) { byte[] data = System.UTF8.GetBytes(message); return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)); } } ``` -------------------------------- ### Load X.509 Certificate in Java Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/zheng-shu-xiang-guan.md This Java method reads an X.509 certificate from a file, checks its validity, and returns it. Ensure the certificate file path is correct. ```java /** * 获取证书。 * * @param filename 证书文件路径 (required) * @return X509证书 */ public static X509Certificate getCertificate(String filename) throws IOException { InputStream fis = new FileInputStream(filename); BufferedInputStream bis = new BufferedInputStream(fis); try { CertificateFactory cf = CertificateFactory.getInstance("X509"); X509Certificate cert = (X509Certificate) cf.generateCertificate(bis); cert.checkValidity(); return cert; } catch (CertificateExpiredException e) { throw new RuntimeException("证书已过期", e); } catch (CertificateNotYetValidException e) { throw new RuntimeException("证书尚未生效", e); } catch (CertificateException e) { throw new RuntimeException("无效的证书文件", e); } finally { bis.close(); } } ``` -------------------------------- ### .Net: 从证书存储区获取私钥签名 Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/qian-ming-xiang-guan.md 此方法从当前用户证书存储区的'My'类别中查找指定主题名称的证书,并使用其RSA私钥对内容进行SHA256签名。适用于.Net。 ```csharp public string Sign(string contentForSign, string subjectName) { X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindBySubjectName, subjectName, false); if (certs.Count > 0) { X509Certificate2 cert = certs[0]; RSA rsa = cert.GetRSAPrivateKey(); var signData = rsa.SignData(Encoding.UTF8.GetBytes(contentForSign), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); return Convert.ToBase64String(signData); } return ""; } ``` -------------------------------- ### Base64解码签名 Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-yan-zheng.md 使用openssl命令将Base64编码的签名解码为原始签名数据,并保存到文件。 ```bash $ openssl base64 -d -A <<< \ 'CtcbzwtQjN8rnOXItEBJ5aQFSnIXESeV28Pr2YEmf9wsDQ8Nx25ytW6FXBCAFdrr0mgqngX3AD9gNzjnNHzSGTPBSsaEkIfhPF4b8YRRTpny88tNLyprXA0GU5ID3DkZHpjFkX1hAp/D0fva2GKjGRLtvYbtUk/OLYqFuzbjt3yOBzJSKQqJsvbXILffgAmX4pKql+Ln+6UPvSCeKwznvtPaEx+9nMBmKu7Wpbqm/+2ksc0XwjD+xlvlECkCxfD/OJ4gN3IurE0fpjxIkvHDiinQmk51BI7zQD8k1znU7r/spPqB+vZjc5ep6DC5wZUpFu5vJ8MoNKjCu8wnzyCFdA==' > signature.txt ``` -------------------------------- ### PHP AES-GCM解密实现 Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/zheng-shu-he-hui-tiao-bao-wen-jie-mi.md 提供了PHP语言中实现AES-GCM解密的工具类,支持sodium和openssl扩展,用于解密微信支付APIv3的回调报文。 ```php class AesUtil { /** * AES key * * @var string */ private $aesKey; const KEY_LENGTH_BYTE = 32; const AUTH_TAG_LENGTH_BYTE = 16; /** * Constructor */ public function __construct($aesKey) { if (strlen($aesKey) != self::KEY_LENGTH_BYTE) { throw new InvalidArgumentException('无效的ApiV3Key,长度应为32个字节'); } $this->aesKey = $aesKey; } /** * Decrypt AEAD_AES_256_GCM ciphertext * * @param string $associatedData AES GCM additional authentication data * @param string $nonceStr AES GCM nonce * @param string $ciphertext AES GCM cipher text * * @return string|bool Decrypted string on success or FALSE on failure */ public function decryptToString($associatedData, $nonceStr, $ciphertext) { $ciphertext = \base64_decode($ciphertext); if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) { return false; } // ext-sodium (default installed on >= PHP 7.2) if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') && \sodium_crypto_aead_aes256gcm_is_available()) { return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey); } // ext-libsodium (need install libsodium-php 1.x via pecl) if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && \Sodium\crypto_aead_aes256gcm_is_available()) { return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey); } // openssl (PHP >= 7.1 support AEAD) if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) { $ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE); $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE); return \openssl_decrypt($ctext, 'aes-256-gcm', $this->aesKey, \OPENSSL_RAW_DATA, $nonceStr, $authTag, $associatedData); } throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php'); } } ``` -------------------------------- ### Load and Parse Private Key from PEM Data Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/qian-ming-xiang-guan.md Use this snippet to decode PEM-encoded private key data and parse it into a private key object. Ensure the PEM data is correctly formatted and of type 'PRIVATE KEY'. ```go package main import ( "crypto/x509" "encoding/pem" "fmt" "log" ) func main() { var pemData = []byte( `----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1yhh6LNB8nXmO SxdGKWmDh0OxAM/wnGyHKSD9tcEhMQTe+wabce0POXzejCmwFBzZa7ZmxH5LoAey T7Fpwb7pptbbDx58CxCYhNEdQ2XrFILUCq3daMj++KQlyDp8U0NspFKsO57gSlih AJ49DzcXQb7Vs5daIvtLapIouPyixAE5uDL+afmJ+bXC11xP5sPWw1RfXynW3vbE yfRol9hQyQWfmO15GSZi6TTAhTKaW31yKaQNChy06K+LsE9JAU+ESxihthtGiMbY 3fFRyhF9Ka2e0wIOz6UdcfwMjxXWRV4OLD1uFG9IYbUiugmYtDyIYZaFDPYdi/+R jm10Ps5lAgMBAAECggEAb19kRZ2lEWOM8D9S//opGZrKPuvneVrsJpZtDuLGcqZM fKvALYXLnZMzzEiE1cpMrmuOMUHaukxNytGGOOupIg7D/SszGv3QahCc6Ne83hwP 1wa/5DDpS0RblIYqRrbgTPQTbk+Mk48Y43K0f2YN82KlHtnLNT7PRDIDX42Nwc1X 8f4JcfyKUE/pOSn+YUlu5Edu6QYbWJWS7mlojEZ/wuWbSymbs6mVVkKeSWGTIh1v 4n2F3Gj6ckUDlt4aZWTVcBa2+ZvSE2h5frSH0snpdGV1bW44IqE3NkwfTQ7JI34C VJdhb3goIyoTmiz6NGEZuiyr8gP9IOjqPfeP7GO5YQKBgQDuB1CT8ksO4SqR3skR kdCQW7kOogZgDThei+3HUMOsHr8L42oYkJDmk2res1ow/mz6SoIV4w6mvvUSnACx dtYA1AzUEs3jvltv8cQ1HAuDhLRslWrhSoxrQQh20yrVxxGN0J4DdCAGURSUwypz UHR+mlfcjacPyxKUsT41+8zG+QKBgQDDg8ZGivuV794RuA3cfpitUFG+0nA0ZS3q AZqlA3ufnCudHQixFIsf83Q7sX7pBob5PNONqsbv0OKpC3/xJRSPIwjWTBUPlDLX rsGajKMhUPtkWo4zkfrSa8XaUpUVDU0qTzS71f9Aab3SkPH1d1o4cQxO08axGLbm TV/46QCBzQKBgDd7ZQDXPT+epHmT4HJD9sVvW9dZVPsWmckP/MC0xqdcE1QGEjjf mablPcfjLma1J1m//Ep1vniHkkBgNJkpBgDzbHoSWAN5335ccEug2d4yFIwq19rj sY9efUaVOirSV/kiY3KSotRWGeIDC+YNHtpTx58VNZes0gvutH2Iz9ahAoGAUcoW b/xEMv0dURxF8C+lfxtSlxlBhymsg3AYWV+Tn7mdJSS4Nhv592vI/A/Mn37zh+BC P8lpX3lq2HzPEPoKF7b4Q22ggdvlSQT6SMT8mTtfbyPSyRAQdWZQZnyVkTD3TvPD g7CKD1As8KFiFuXPAD2KgI9nVz6XhNBpjZ8rbyECgYEAsOrm1hbNZbvlNhnuUjw5 DTgTuJ3B0j1aK/7C2EQWR+mIG2q5TKDC6xNdszV0gK1/TbJk4RNgQo0JLkuZ2Xk2 Q8KhaNe+X8SYP9CFKIsXuhGrYI5ICjipov5oJqjESV4wle575eWwdPgF1ICabpIq dnX2MxS9tkk830uXxPrXpRA= -----END PRIVATE KEY----- `) block, rest := pem.Decode(pemData) if block == nil || block.Type != "PRIVATE KEY" { log.Fatal("failed to decode PEM block containing public key") } pri, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { log.Fatal(err) } fmt.Printf("Got a %T, with remaining data: %q", pri, rest) } ``` -------------------------------- ### URL for Signature Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-sheng-cheng.md The second line is the absolute URL of the request, excluding the domain name. Query parameters should be appended if present. ```http /v3/certificates ``` -------------------------------- ### 加密报文格式示例 Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/zheng-shu-he-hui-tiao-bao-wen-jie-mi.md 展示了微信支付APIv3中加密报文的JSON对象结构,包括原始类型、加密算法、密文、初始化向量和附加数据。 ```json { "original_type": "transaction", // 加密前的对象类型 "algorithm":"AEAD_AES_256_GCM", // 加密算法 // Base64编码后的密文 "ciphertext": "...", // 加密使用的随机串初始化向量) "nonce": "...", // 附加数据包(可能为空字符串) "associated_data": "" } ``` -------------------------------- ### Java AES-GCM解密实现 Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/zheng-shu-he-hui-tiao-bao-wen-jie-mi.md 提供了Java语言中实现AES-GCM解密的工具类,用于解密微信支付APIv3的回调报文。 ```java import java.io.IOException; import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; public class AesUtil { static final int KEY_LENGTH_BYTE = 32; static final int TAG_LENGTH_BIT = 128; private final byte[] aesKey; public AesUtil(byte[] key) { if (key.length != KEY_LENGTH_BYTE) { throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节"); } this.aesKey = key; } public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext) throws GeneralSecurityException, IOException { try { Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); SecretKeySpec key = new SecretKeySpec(aesKey, "AES"); GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce); cipher.init(Cipher.DECRYPT_MODE, key, spec); cipher.updateAAD(associatedData); return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8"); } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { throw new IllegalStateException(e); } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { throw new IllegalArgumentException(e); } } } ``` -------------------------------- ### Generate Request Timestamp Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-sheng-cheng.md The third line is the current system timestamp (seconds since epoch). Ensure your system time is accurate. ```bash date +%s 1554208460 ``` -------------------------------- ### .NET RSA Encryption Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/min-gan-xin-xi-jia-mi.md Encrypts a string using RSA with PKCS#1 OAEP padding. Takes the text to encrypt and the public key as a byte array. Converts the result to a Base64 string. ```csharp /// /// 最终提交请求时,需对敏感信息加密,如身份证、银行卡号。 /// 加密算法是RSA,使用从接口下载到的公钥进行加密,非后台下载到的私钥。 /// 感谢ISV(https://github.com/ndma-isv)提供示例 /// /// /// 要加密的明文 /// -----BEGIN CERTIFICATE----- 开头的string,转为bytes /// public static string RSAEncrypt(string text, byte[] publicKey) { using (var x509 = new X509Certificate2(publicKey)) { using (var rsa = (RSACryptoServiceProvider)x509.PublicKey.Key) { var buff = rsa.Encrypt(Encoding.UTF8.GetBytes(text), true); return Convert.ToBase64String(buff); } } } ``` -------------------------------- ### View Certificate Serial Number with OpenSSL Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/zheng-shu-xiang-guan.md Use the openssl command-line tool to extract the serial number from a certificate file. ```bash $ openssl x509 -in 1900009191_20180326_cert.pem -noout -serial serial=1DDE55AD98ED71D6EDD4A4A16996DE7B47773A8C ``` -------------------------------- ### 构造验签名串示例 Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-yan-zheng.md 按照微信支付的规则,将应答时间戳、应答随机串和应答报文主体拼接成验签名串,每行以\n结束。 ```text 1554209980 c5ac7061fccab6bf3e254dcf98995b8c {"data":[{"serial_no":"5157F09EFDC096DE15EBE81A47057A7232F1B8E1","effective_time":"2018-03-26T11:39:50+08:00","expire_time":"2023-03-25T11:39:50+08:00","encrypt_certificate":{"algorithm":"AEAD_AES_256_GCM","nonce":"4de73afd28b6","associated_data":"certificate","ciphertext":"..."}}]} ``` -------------------------------- ### Read X.509 Certificate in PHP Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/zheng-shu-xiang-guan.md This PHP function reads an X.509 certificate from a specified file path. It returns a certificate resource identifier or FALSE on failure. ```php /** * Read certificate from file * * @param string $filepath PEM encoded X.509 certificate file path * * @return resource|bool X.509 certificate resource identifier on success or FALSE on failure */ public static function getCertificate($filepath) { return openssl_x509_read(file_get_contents($filepath)); } ``` -------------------------------- ### Convert PKCS7 Certificate Trust Chain to PEM Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/zheng-shu-xiang-guan.md Convert a PKCS7 certificate trust chain file (CertTrustChain.p7b) into PEM format using the openssl command-line tool. ```bash openssl pkcs7 -print_certs -in CertTrustChain.p7b -inform der -out CertTrustChain.pem ``` -------------------------------- ### Go RSA OAEP Encryption Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/min-gan-xin-xi-jia-mi.md Encrypts a byte slice message using RSA with OAEP padding and SHA-1. Requires a random number generator and the RSA public key. Handles potential encryption errors. ```go secretMessage := []byte("send reinforcements, we're going to advance") rng := rand.Reader cipherdata, err := EncryptOAEP(sha1.New(), rng, rsaPublicKey, secretMessage, nil) if err != nil { fmt.Fprintf(os.Stderr, "Error from encryption: %s\n", err) return } ciphertext := base64.StdEncoding.EncodeToString(cipherdata) fmt.Printf("Ciphertext: %s\n", ciphertext) ``` -------------------------------- ### Enable Unlimited Strength Policy in Java Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/chang-jian-wen-ti/api-v3-mi-yao-xiang-guan.md Use this code to bypass Java's default cryptographic limitations for AES encryption when using older Java versions (8u151 and 8u152). Ensure you understand the security implications. ```java Security.setProperty("crypto.policy", "unlimited"); ``` -------------------------------- ### Generate Request Random String Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-sheng-cheng.md The fourth line is a randomly generated string for the request. Refer to the provided algorithm for generation. ```bash hexdump -n 16 -e '4/4 "%08X" 1 "\n"' /dev/random 593BEC0C930BF1AFEB40B4A08C8FB242 ``` -------------------------------- ### API Request Headers Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/wei-xin-zhi-fu-api-v3-jie-kou-gui-fan.md All API requests must use HTTPS. The Content-Type and Accept headers should be set to application/json for most requests, with exceptions for image uploads. ```APIDOC ## API Request Headers ### Description All API requests must use HTTPS. The `Content-Type` and `Accept` headers should be set to `application/json` for most requests, with exceptions for image uploads. ### Headers - `Content-Type`: `application/json` - `Accept`: `application/json` ``` -------------------------------- ### Java HTTP Client for WeChat Pay API v3 Source: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/wei-xin-zhi-fu-ping-tai-zheng-shu-geng-xin-zhi-yin.md This Java library assists with WeChat Pay API v3 interactions, including automatic certificate updates. Ensure you are using version v0.1.5 or later for the AutoUpdateCertificatewsVerifier functionality. ```Java com.wechatpay.apache.httpclient.util.AutoUpdateCertificatesVerifier ```