Login expired. Please log in again.

Feedback

0/300

Feedback

Submitted successfully

ok

Feedback

Network exception, please try again later

ok

Certificate and decrypting the callback message

To ensure security, WeChat Pay uses AES-256-GCM encryption for key information in the callback notification and platform certificate download API. This chapter details the format of encrypted messages and how to decrypt them.

1. Format of Encrypted Messages

AES-AES-GCM is an Authenticated Encryption algorithm of the NIST standard and an encryption mode that can guarantee the confidentiality, integrity and authenticity of data.It is most widely used in TLS.

The encryption key used in the certificate and callback messages is the API V3 key.

We use a separate JSON object to represent encrypted data. For ease of reading, the example is Pretty formatted and comments are added.

{
	"original_type": "transaction", //Object type before encryption
	"algorithm": "AEAD_AES_256_GCM", // Encryption algorithm
	// Base64 encoded ciphertext
	"ciphertext": "...",
	// Random string initialization vector used for encryption
	"nonce": "...",
	// Additional data package (can be left blank)
	"associated_data": ""
}
Notice
The encrypted random string is irrelevant with and different from the random string used when signing.

2. Decryption

Please refer to RFC 5116 for details of the algorithm API.

Most programming languages (newer versions) support AEAD_AES_256_GCM. Developers can refer to the following examples to learn how to use your programming language for decryption.

package com.wechat.v3;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.wechat.pay.contrib.apache.httpclient.exception.ParseException;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;

import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;


class DecodeCipher {

    private static final String apiV3Key = "";

    private void setDecryptData(Notification notification) throws ParseException {

        Notification.Resource resource = notification.getResource();
        String getAssociateddData = "";
        if (resource.getAssociatedData() != null) {
            getAssociateddData = resource.getAssociatedData();
        }
        byte[] associatedData = getAssociateddData.getBytes(StandardCharsets.UTF_8);
        byte[] nonce = resource.getNonce().getBytes(StandardCharsets.UTF_8);
        String ciphertext = resource.getCiphertext();
        AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
        String decryptData;
        try {
            decryptData = aesUtil.decryptToString(associatedData, nonce, ciphertext);
        } catch (GeneralSecurityException e) {
            throw new ParseException("AES解密失败,resource:" + resource.toString(), e);
        }
        notification.setDecryptData(decryptData);
    }


    @JsonIgnoreProperties(ignoreUnknown = true)
    public class Notification {

        @JsonProperty("id")
        private String id;
        @JsonProperty("create_time")
        private String createTime;
        @JsonProperty("event_type")
        private String eventType;
        @JsonProperty("resource_type")
        private String resourceType;
        @JsonProperty("summary")
        private String summary;
        @JsonProperty("resource")
        private Resource resource;
        private String decryptData;

        @Override
        public String toString() {
            return "Notification{" +
                    "id='" + id + '\'' +
                    ", createTime='" + createTime + '\'' +
                    ", eventType='" + eventType + '\'' +
                    ", resourceType='" + resourceType + '\'' +
                    ", decryptData='" + decryptData + '\'' +
                    ", summary='" + summary + '\'' +
                    ", resource=" + resource +
                    '}';
        }

        public String getId() {
            return id;
        }

        public String getCreateTime() {
            return createTime;
        }

        public String getEventType() {
            return eventType;
        }

        public String getDecryptData() {
            return decryptData;
        }

        public String getSummary() {
            return summary;
        }

        public String getResourceType() {
            return resourceType;
        }

        public Resource getResource() {
            return resource;
        }

        public void setDecryptData(String decryptData) {
            this.decryptData = decryptData;
        }

        @JsonIgnoreProperties(ignoreUnknown = true)
        public class Resource {

            @JsonProperty("algorithm")
            private String algorithm;
            @JsonProperty("ciphertext")
            private String ciphertext;
            @JsonProperty("associated_data")
            private String associatedData;
            @JsonProperty("nonce")
            private String nonce;
            @JsonProperty("original_type")
            private String originalType;

            public String getAlgorithm() {
                return algorithm;
            }

            public String getCiphertext() {
                return ciphertext;
            }

            public String getAssociatedData() {
                return associatedData;
            }

            public String getNonce() {
                return nonce;
            }

            public String getOriginalType() {
                return originalType;
            }

            @Override
            public String toString() {
                return "Resource{" +
                        "algorithm='" + algorithm + '\'' +
                        ", ciphertext='" + ciphertext + '\'' +
                        ", associatedData='" + associatedData + '\'' +
                        ", nonce='" + nonce + '\'' +
                        ", originalType='" + originalType + '\'' +
                        '}';
            }
        }

    }
}
<?php
require_once('vendor/autoload.php');

use WeChatPay\Crypto\Rsa;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Formatter;

$inWechatpaySignature = '';// Get this value from the header in response
$inWechatpayTimestamp = '';// Get this value from the header in response
$inWechatpaySerial = '';// Get this value from the header in response
$inWechatpayNonce = '';// Get this value from the header in response
$inBody = '';// Get this value from the body in response
$apiv3Key = '';
$platformPublicKeyInstance = Rsa::from('file:///path/to/wechatpay/inWechatpaySerial.pem', Rsa::KEY_TYPE_PUBLIC);

$timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
$verifiedStatus = Rsa::verify(
    Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
    $inWechatpaySignature,
    $platformPublicKeyInstance
);
if ($timeOffsetStatus && $verifiedStatus) {
    $inBodyArray = (array)json_decode($inBody, true);
    ['resource' => [
        'ciphertext'      => $ciphertext,
        'nonce'           => $nonce,
        'associated_data' => $aad
    ]] = $inBodyArray;
    $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
    $inBodyResourceArray = (array)json_decode($inBodyResource, true);
}
package wechatpay

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
)

// DecryptAES256GCM 使用 AEAD_AES_256_GCM 算法进行解密
//
// 你可以使用此算法完成微信支付平台证书和回调报文解密,详见:
// https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/zheng-shu-he-hui-tiao-bao-wen-jie-mi
func DecryptAES256GCM(aesKey, associatedData, nonce, ciphertext string) (plaintext string, err error) {
	decodedCiphertext, err := base64.StdEncoding.DecodeString(ciphertext)
	if err != nil {
		return "", err
	}
	c, err := aes.NewCipher([]byte(aesKey))
	if err != nil {
		return "", err
	}
	gcm, err := cipher.NewGCM(c)
	if err != nil {
		return "", err
	}
	dataBytes, err := gcm.Open(nil, []byte(nonce), decodedCiphertext, []byte(associatedData))
	if err != nil {
		return "", err
	}
	return string(dataBytes), nil
}
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import base64

def decrypt(nonce, ciphertext, associated_data):
    key = "Your32Apiv3Key"

    key_bytes = str.encode(key)
    nonce_bytes = str.encode(nonce)
    ad_bytes = str.encode(associated_data)
    data = base64.b64decode(ciphertext)

    aesgcm = AESGCM(key_bytes)
    return aesgcm.decrypt(nonce_bytes, data, ad_bytes)
    Page Navigation

About  WeChat  Pay

Powered By Tencent & Tenpay Copyright©

2005-2024 Tenpay All Rights Reserved.

Contact Us
Wechat Pay Global

WeChat Pay Global

置顶