Login expired. Please log in again.

Feedback

0/300

Feedback

Submitted successfully

ok

Feedback

Network exception, please try again later

ok

Encrypting and Decrypting Sensitive Information

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,useCipher.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.

package com.wechat.v3;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Base64;

public class EncryptionUtil {

private static final String TRANSFORMATION = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";

public static String encryptOAEP(String message, X509Certificate certificate) throws IllegalBlockSizeException {
return encrypt(message, certificate, TRANSFORMATION);
}

public static String encrypt(String message, X509Certificate certificate, String transformation) throws IllegalBlockSizeException {
try {
Cipher cipher = Cipher.getInstance(transformation);
cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());
byte[] data = message.getBytes(StandardCharsets.UTF_8);
byte[] ciphertext = cipher.doFinal(data);
return Base64.getEncoder().encodeToString(ciphertext);

} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
} catch (InvalidKeyException e) {
throw new IllegalArgumentException("无效的证书", e);
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
}
}
}
<?php declare(strict_types=1);

namespace WeChatPay\Crypto;

use const OPENSSL_PKCS1_OAEP_PADDING;
use const OPENSSL_PKCS1_PADDING;
use function base64_encode;
use function openssl_public_encrypt;
use function sprintf;

use UnexpectedValueException;

class Rsa
{
    
    private static function paddingModeLimitedCheck(int $padding): void
    {
        if (!($padding === OPENSSL_PKCS1_OAEP_PADDING || $padding === OPENSSL_PKCS1_PADDING)) {
            throw new UnexpectedValueException(sprintf("Doesn't supported padding mode(%d), here only support OPENSSL_PKCS1_OAEP_PADDING or OPENSSL_PKCS1_PADDING.", $padding));
        }
    }

    public static function encrypt(string $plaintext, $publicKey, int $padding = OPENSSL_PKCS1_OAEP_PADDING): string
    {
        self::paddingModeLimitedCheck($padding);

        if (!openssl_public_encrypt($plaintext, $encrypted, $publicKey, $padding)) {
            throw new UnexpectedValueException('Encrypting the input $plaintext failed, please checking your $publicKey whether or nor correct.');
        }

        return base64_encode($encrypted);
    }
}
package wechatpay

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/x509"
	"encoding/base64"
	"fmt"
)

// EncryptOAEPWithPublicKey 使用 OAEP padding方式用公钥进行加密
func EncryptOAEPWithPublicKey(message string, publicKey *rsa.PublicKey) (ciphertext string, err error) {
	if publicKey == nil {
		return "", fmt.Errorf("you should input *rsa.PublicKey")
	}
	ciphertextByte, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, publicKey, []byte(message), nil)
	if err != nil {
		return "", fmt.Errorf("encrypt message with public key err:%s", err.Error())
	}
	ciphertext = base64.StdEncoding.EncodeToString(ciphertextByte)
	return ciphertext, nil
}

// EncryptOAEPWithCertificate 先解析出证书中的公钥,然后使用 OAEP padding方式公钥进行加密
func EncryptOAEPWithCertificate(message string, certificate *x509.Certificate) (ciphertext string, err error) {
	if certificate == nil {
		return "", fmt.Errorf("you should input *x509.Certificate")
	}
	publicKey, ok := certificate.PublicKey.(*rsa.PublicKey)
	if !ok {
		return "", fmt.Errorf("certificate is invalid")
	}
	return EncryptOAEPWithPublicKey(message, publicKey)
}

// EncryptPKCS1v15WithPublicKey 使用PKCS1 padding方式用公钥进行加密
func EncryptPKCS1v15WithPublicKey(message string, publicKey *rsa.PublicKey) (ciphertext string, err error) {
	if publicKey == nil {
		return "", fmt.Errorf("you should input *rsa.PublicKey")
	}
	ciphertextByte, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, []byte(message))
	if err != nil {
		return "", fmt.Errorf("encrypt message with public key err:%s", err.Error())
	}
	ciphertext = base64.StdEncoding.EncodeToString(ciphertextByte)
	return ciphertext, nil
}

// EncryptPKCS1v15WithCertificate 先解析出证书中的公钥,然后使用PKCS1 padding方式用公钥进行加密
func EncryptPKCS1v15WithCertificate(message string, certificate *x509.Certificate) (ciphertext string, err error) {
	if certificate == nil {
		return "", fmt.Errorf("you should input *x509.Certificate")
	}
	publicKey, ok := certificate.PublicKey.(*rsa.PublicKey)
	if !ok {
		return "", fmt.Errorf("certificate is invalid")
	}
	return EncryptPKCS1v15WithPublicKey(message, publicKey)
}

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.

package com.wechat.v3;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.util.Base64;

public class DecryptionUtil {

    private static final String TRANSFORMATION = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";

    public static String decryptOAEP(String ciphertext, PrivateKey privateKey) throws BadPaddingException {
        return decrypt(ciphertext, privateKey, TRANSFORMATION);
    }

    public static String decrypt(String ciphertext, PrivateKey privateKey, String transformation) throws BadPaddingException {
        try {
            Cipher cipher = Cipher.getInstance(transformation);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] data = Base64.getDecoder().decode(ciphertext);
            return new String(cipher.doFinal(data), StandardCharsets.UTF_8);

        } catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("无效的私钥", e);
        } catch (BadPaddingException | IllegalBlockSizeException e) {
            throw new BadPaddingException("解密失败");
        }
    }
}
<?php declare(strict_types=1);

namespace WeChatPay\Crypto;

use const OPENSSL_PKCS1_OAEP_PADDING;
use const OPENSSL_PKCS1_PADDING;
use function base64_decode;
use function openssl_private_decrypt;
use function sprintf;

use UnexpectedValueException;

class Rsa
{
    
    private static function paddingModeLimitedCheck(int $padding): void
    {
        if (!($padding === OPENSSL_PKCS1_OAEP_PADDING || $padding === OPENSSL_PKCS1_PADDING)) {
            throw new UnexpectedValueException(sprintf("Doesn't supported padding mode(%d), here only support OPENSSL_PKCS1_OAEP_PADDING or OPENSSL_PKCS1_PADDING.", $padding));
        }
    }

    public static function decrypt(string $ciphertext, $privateKey, int $padding = OPENSSL_PKCS1_OAEP_PADDING): string
    {
        self::paddingModeLimitedCheck($padding);

        if (!openssl_private_decrypt(base64_decode($ciphertext), $decrypted, $privateKey, $padding)) {
            throw new UnexpectedValueException('Decrypting the input $ciphertext failed, please checking your $privateKey whether or nor correct.');
        }

        return $decrypted;
    }
}
package wechatpay

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
)

// DecryptOAEP 使用私钥进行解密
func DecryptOAEP(ciphertext string, privateKey *rsa.PrivateKey) (message string, err error) {
	if privateKey == nil {
		return "", fmt.Errorf("you should input *rsa.PrivateKey")
	}
	decodedCiphertext, err := base64.StdEncoding.DecodeString(ciphertext)
	if err != nil {
		return "", fmt.Errorf("base64 decode failed, error=%s", err.Error())
	}
	messageBytes, err := rsa.DecryptOAEP(sha1.New(), rand.Reader, privateKey, decodedCiphertext, nil)
	if err != nil {
		return "", fmt.Errorf("decrypt ciphertext with private key err:%s", err)
	}
	return string(messageBytes), nil
}

// DecryptPKCS1v15 使用私钥对PKCS1 padding方式加密的字符串进行解密
func DecryptPKCS1v15(ciphertext string, privateKey *rsa.PrivateKey) (message string, err error) {
	if privateKey == nil {
		return "", fmt.Errorf("you should input *rsa.PrivateKey")
	}
	decodedCiphertext, err := base64.StdEncoding.DecodeString(ciphertext)
	if err != nil {
		return "", fmt.Errorf("base64 decode failed, error=%s", err.Error())
	}
	messageBytes, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, decodedCiphertext)
	if err != nil {
		return "", fmt.Errorf("decrypt ciphertext with private key err:%s", err)
	}
	return string(messageBytes), nil
}
    Page Navigation

About  WeChat  Pay

Powered By Tencent & Tenpay Copyright©

2005-2024 Tenpay All Rights Reserved.

Contact Us
Wechat Pay Global

WeChat Pay Global

置顶