To ensure the confidentiality of sensitive information fields (such as the user's address, bank card number, and mobile phone number) during the communication process, WeChat Pay API V3 requires merchants to encrypt the sensitive information fields sent. Accordingly, WeChat Pay will encrypt the downstream sensitive information fields, and merchants will need to decrypt them before they can get the original text. Information about the encryption and decryption methods, as well as how to perform the corresponding calculations, is provided below.
1. Encryption Algorithm
RSA public key encryption algorithm, is used to encrypt sensitive information. We use the more secure Optimal Asymmetric Encryption Padding (RSAES-OAEP) as the padding solution used by the encryption algorithm.
The mode value of RSAES-OAEP in each programming language is:
Developers should use the public key in the WeChat Pay platform certificate to encrypt sensitive information. By doing so, only WeChat payment transactions with the private key can decrypt the ciphertext, thereby ensuring the confidentiality of the information.
On the other hand, WeChat Pay uses the public key in the merchant certificate to encrypt downstream sensitive information. Developers should use the merchant's private key to decrypt the ciphertext of the downstream sensitive information.
2. Encryption Example
Developers should use the public key in the WeChat Pay platform certificate to encrypt sensitive information.
Most programming languages support RSA public key encryption. You can refer to the examples to learn how to use your programming language to encrypt sensitive information.
JAVA

1package com.wechat.v3;
2
3import javax.crypto.BadPaddingException;
4import javax.crypto.Cipher;
5import javax.crypto.IllegalBlockSizeException;
6import javax.crypto.NoSuchPaddingException;
7import java.nio.charset.StandardCharsets;
8import java.security.InvalidKeyException;
9import java.security.NoSuchAlgorithmException;
10import java.security.cert.X509Certificate;
11import java.util.Base64;
12
13public class EncryptionUtil {
14
15private static final String TRANSFORMATION = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
16
17public static String encryptOAEP(String message, X509Certificate certificate) throws IllegalBlockSizeException {
18return encrypt(message, certificate, TRANSFORMATION);
19}
20
21public static String encrypt(String message, X509Certificate certificate, String transformation) throws IllegalBlockSizeException {
22try {
23Cipher cipher = Cipher.getInstance(transformation);
24cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());
25byte[] data = message.getBytes(StandardCharsets.UTF_8);
26byte[] ciphertext = cipher.doFinal(data);
27return Base64.getEncoder().encodeToString(ciphertext);
28
29} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
30throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
31} catch (InvalidKeyException e) {
32throw new IllegalArgumentException("无效的证书", e);
33} catch (IllegalBlockSizeException | BadPaddingException e) {
34throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
35}
36}
37}

1<?php declare(strict_types=1);
2
3namespace WeChatPay\Crypto;
4
5use const OPENSSL_PKCS1_OAEP_PADDING;
6use const OPENSSL_PKCS1_PADDING;
7use function base64_encode;
8use function openssl_public_encrypt;
9use function sprintf;
10
11use UnexpectedValueException;
12
13class Rsa
14{
15
16 private static function paddingModeLimitedCheck(int $padding): void
17 {
18 if (!($padding === OPENSSL_PKCS1_OAEP_PADDING || $padding === OPENSSL_PKCS1_PADDING)) {
19 throw new UnexpectedValueException(sprintf("Doesn't supported padding mode(%d), here only support OPENSSL_PKCS1_OAEP_PADDING or OPENSSL_PKCS1_PADDING.", $padding));
20 }
21 }
22
23 public static function encrypt(string $plaintext, $publicKey, int $padding = OPENSSL_PKCS1_OAEP_PADDING): string
24 {
25 self::paddingModeLimitedCheck($padding);
26
27 if (!openssl_public_encrypt($plaintext, $encrypted, $publicKey, $padding)) {
28 throw new UnexpectedValueException('Encrypting the input $plaintext failed, please checking your $publicKey whether or nor correct.');
29 }
30
31 return base64_encode($encrypted);
32 }
33}
GO

1package wechatpay
2
3import (
4 "crypto/rand"
5 "crypto/rsa"
6 "crypto/sha1"
7 "crypto/x509"
8 "encoding/base64"
9 "fmt"
10)
11
12
13func EncryptOAEPWithPublicKey(message string, publicKey *rsa.PublicKey) (ciphertext string, err error) {
14 if publicKey == nil {
15 return "", fmt.Errorf("you should input *rsa.PublicKey")
16 }
17 ciphertextByte, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, publicKey, []byte(message), nil)
18 if err != nil {
19 return "", fmt.Errorf("encrypt message with public key err:%s", err.Error())
20 }
21 ciphertext = base64.StdEncoding.EncodeToString(ciphertextByte)
22 return ciphertext, nil
23}
24
25
26func EncryptOAEPWithCertificate(message string, certificate *x509.Certificate) (ciphertext string, err error) {
27 if certificate == nil {
28 return "", fmt.Errorf("you should input *x509.Certificate")
29 }
30 publicKey, ok := certificate.PublicKey.(*rsa.PublicKey)
31 if !ok {
32 return "", fmt.Errorf("certificate is invalid")
33 }
34 return EncryptOAEPWithPublicKey(message, publicKey)
35}
36
37
38func EncryptPKCS1v15WithPublicKey(message string, publicKey *rsa.PublicKey) (ciphertext string, err error) {
39 if publicKey == nil {
40 return "", fmt.Errorf("you should input *rsa.PublicKey")
41 }
42 ciphertextByte, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, []byte(message))
43 if err != nil {
44 return "", fmt.Errorf("encrypt message with public key err:%s", err.Error())
45 }
46 ciphertext = base64.StdEncoding.EncodeToString(ciphertextByte)
47 return ciphertext, nil
48}
49
50
51func EncryptPKCS1v15WithCertificate(message string, certificate *x509.Certificate) (ciphertext string, err error) {
52 if certificate == nil {
53 return "", fmt.Errorf("you should input *x509.Certificate")
54 }
55 publicKey, ok := certificate.PublicKey.(*rsa.PublicKey)
56 if !ok {
57 return "", fmt.Errorf("certificate is invalid")
58 }
59 return EncryptPKCS1v15WithPublicKey(message, publicKey)
60}
In some cases, WeChat Pay will update the platform certificate. In that case, merchants will have multiple WeChat Payt platform certificates to use for encryption. To ensure proper decryption, the HTTP header of the request initiated by the merchant should include the RSA public key encryption algorithm to declare the key pair and certificate used in encryption.
4. Decryption Example
WeChat Pay uses the public key in the merchant certificate to encrypt downstream sensitive information. The developer should use the merchant's private key to decrypt the ciphertext of the downstream sensitive information.
Similarly, most programming languages support RSA private key decryption. You can refer to the examples to learn how to use your programming language to decrypt sensitive information.
JAVA

1package com.wechat.v3;
2
3import javax.crypto.BadPaddingException;
4import javax.crypto.Cipher;
5import javax.crypto.IllegalBlockSizeException;
6import javax.crypto.NoSuchPaddingException;
7import java.nio.charset.StandardCharsets;
8import java.security.InvalidKeyException;
9import java.security.NoSuchAlgorithmException;
10import java.security.PrivateKey;
11import java.util.Base64;
12
13public class DecryptionUtil {
14
15 private static final String TRANSFORMATION = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
16
17 public static String decryptOAEP(String ciphertext, PrivateKey privateKey) throws BadPaddingException {
18 return decrypt(ciphertext, privateKey, TRANSFORMATION);
19 }
20
21 public static String decrypt(String ciphertext, PrivateKey privateKey, String transformation) throws BadPaddingException {
22 try {
23 Cipher cipher = Cipher.getInstance(transformation);
24 cipher.init(Cipher.DECRYPT_MODE, privateKey);
25 byte[] data = Base64.getDecoder().decode(ciphertext);
26 return new String(cipher.doFinal(data), StandardCharsets.UTF_8);
27
28 } catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
29 throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
30 } catch (InvalidKeyException e) {
31 throw new IllegalArgumentException("无效的私钥", e);
32 } catch (BadPaddingException | IllegalBlockSizeException e) {
33 throw new BadPaddingException("解密失败");
34 }
35 }
36}
PHP

1<?php declare(strict_types=1);
2
3namespace WeChatPay\Crypto;
4
5use const OPENSSL_PKCS1_OAEP_PADDING;
6use const OPENSSL_PKCS1_PADDING;
7use function base64_decode;
8use function openssl_private_decrypt;
9use function sprintf;
10
11use UnexpectedValueException;
12
13class Rsa
14{
15
16 private static function paddingModeLimitedCheck(int $padding): void
17 {
18 if (!($padding === OPENSSL_PKCS1_OAEP_PADDING || $padding === OPENSSL_PKCS1_PADDING)) {
19 throw new UnexpectedValueException(sprintf("Doesn't supported padding mode(%d), here only support OPENSSL_PKCS1_OAEP_PADDING or OPENSSL_PKCS1_PADDING.", $padding));
20 }
21 }
22
23 public static function decrypt(string $ciphertext, $privateKey, int $padding = OPENSSL_PKCS1_OAEP_PADDING): string
24 {
25 self::paddingModeLimitedCheck($padding);
26
27 if (!openssl_private_decrypt(base64_decode($ciphertext), $decrypted, $privateKey, $padding)) {
28 throw new UnexpectedValueException('Decrypting the input $ciphertext failed, please checking your $privateKey whether or nor correct.');
29 }
30
31 return $decrypted;
32 }
33}
GO

1package wechatpay
2
3import (
4 "crypto/rand"
5 "crypto/rsa"
6 "crypto/sha1"
7 "encoding/base64"
8 "fmt"
9)
10
11
12func DecryptOAEP(ciphertext string, privateKey *rsa.PrivateKey) (message string, err error) {
13 if privateKey == nil {
14 return "", fmt.Errorf("you should input *rsa.PrivateKey")
15 }
16 decodedCiphertext, err := base64.StdEncoding.DecodeString(ciphertext)
17 if err != nil {
18 return "", fmt.Errorf("base64 decode failed, error=%s", err.Error())
19 }
20 messageBytes, err := rsa.DecryptOAEP(sha1.New(), rand.Reader, privateKey, decodedCiphertext, nil)
21 if err != nil {
22 return "", fmt.Errorf("decrypt ciphertext with private key err:%s", err)
23 }
24 return string(messageBytes), nil
25}
26
27
28func DecryptPKCS1v15(ciphertext string, privateKey *rsa.PrivateKey) (message string, err error) {
29 if privateKey == nil {
30 return "", fmt.Errorf("you should input *rsa.PrivateKey")
31 }
32 decodedCiphertext, err := base64.StdEncoding.DecodeString(ciphertext)
33 if err != nil {
34 return "", fmt.Errorf("base64 decode failed, error=%s", err.Error())
35 }
36 messageBytes, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, decodedCiphertext)
37 if err != nil {
38 return "", fmt.Errorf("decrypt ciphertext with private key err:%s", err)
39 }
40 return string(messageBytes), nil
41}