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)