Encrypting and Decrypting Sensitive Information

Update Time:2024.09.18

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:

  • OpenSSL,padding is set toRSA_PKCS1_OAEP_PADDING

  • Java,use Cipher.getinstance(RSA/ECB/OAEPWithSHA-1AndMGF1Padding)

  • PHP,padding is set toOPENSSL_PKCS1_OAEP_PADDING

  • .NET,fOAEP is set totrue

  • Node.js,padding is set tocrypto.constants.RSA_PKCS1_OAEP_PADDING

  • Go,use EncryptOAEP

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}

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_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// EncryptOAEPWithPublicKey 使用 OAEP padding方式用公钥进行加密
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// EncryptOAEPWithCertificate 先解析出证书中的公钥,然后使用 OAEP padding方式公钥进行加密
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// EncryptPKCS1v15WithPublicKey 使用PKCS1 padding方式用公钥进行加密
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// EncryptPKCS1v15WithCertificate 先解析出证书中的公钥,然后使用PKCS1 padding方式用公钥进行加密
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}

3. Declaring the Platform Certificate used in Encryption

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.

  • Merchants use WeChat Pay platform public key encryption when sending sensitive information. The certificate serial number is included in Wechatpay-Serial in the HTTP header of the request.

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// DecryptOAEP 使用私钥进行解密
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// DecryptPKCS1v15 使用私钥对PKCS1 padding方式加密的字符串进行解密
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}

 

About  WeChat  Pay

Powered By Tencent & Tenpay Copyright©

2005-2025 Tenpay All Rights Reserved.

Contact Us
Wechat Pay Global

WeChat Pay Global

Contact Us

Customer Service Tel

+86 571 95017

9:00-18:00 Monday-Friday GMT+8

Business Development

wxpayglobal@tencent.com

Developer Support

wepayTS@tencent.com

Wechat Pay Global

About Tenpay
Powered By Tencent & Tenpay Copyright© 2005-2025 Tenpay All Rights Reserved.