为了保证通信过程中敏感信息字段(如用户的住址、银行卡号、手机号码等)的机密性,微信支付API v3要求商户对上送的敏感信息字段进行加密。与之相对应,微信支付会对下行的敏感信息字段进行加密,商户需解密后方能得到原文。下面详细介绍加解密的方式,以及如何进行相应的计算。
我们提供了微信支付API v3官方SDK(目前包含 Java 、 PHP 、 Go 三种语言版本),使用官方 SDK 调用微信支付接口,无需关心签名生成和验证,接入更方便。
使用平台证书加密,需要在http请求增加一个Wechatpay-Serial请求头,并传入平台证书序列号"Wechatpay-Serial: 4A4EF961F12345D888A902944179F1F270FC68B3"(此处的平台证书序列号只是一个示例,每个商户号的平台证书序列号不一样,需传入商户号对应的平台证书序列号)
1. 加密算法 敏感信息加密使用的 RSA公钥加密算法 。加密算法使用的填充方案,我们使用了相对更安全的RSAES-OAEP(Optimal Asymmetric Encryption Padding)。
OpenSSL ,padding设置为 RSA_PKCS1_OAEP_PADDING
Java,使用 Cipher.getinstance(RSA/ECB/OAEPWithSHA-1AndMGF1Padding)
PHP ,padding设置为 OPENSSL_PKCS1_OAEP_PADDING
.NET ,fOAEP设置为true
Node.js ,padding设置为 crypto.constants.RSA_PKCS1_OAEP_PADDING
GO ,使用 EncryptOAEP
开发者应当使用微信支付平台证书中的公钥,对上送的敏感信息进行加密。这样只有拥有私钥的微信支付才能对密文进行解密,从而保证了信息的机密性。
另一方面,微信支付使用 商户证书中的公钥对下行的敏感信息进行加密。开发者应使用商户私钥对下行的敏感信息的密文进行解密。
2. 加密示例 开发者应当使用微信支付平台证书中的公钥,对上送的敏感信息进行加密。
大部分编程语言支持RSA公钥加密。你可以参考示例,了解如何使用您的编程语言实现敏感信息加密。
1 public static String rsaEncryptOAEP ( String message , X509Certificate certificate )
2 throws IllegalBlockSizeException , IOException {
3 try {
4 Cipher cipher = Cipher . getInstance ( "RSA/ECB/OAEPWithSHA-1AndMGF1Padding" ) ;
5 cipher . init ( Cipher . ENCRYPT_MODE , certificate . getPublicKey ( ) ) ;
6 byte [ ] data = message . getBytes ( "utf-8" ) ;
7 byte [ ] cipherdata = cipher . doFinal ( data ) ;
8 return Base64 . getEncoder ( ) . encodeToString ( cipherdata ) ;
9 } catch ( NoSuchAlgorithmException | NoSuchPaddingException e ) {
10 throw new RuntimeException ( "当前Java环境不支持RSA v1.5/OAEP" , e ) ;
11 } catch ( InvalidKeyException e ) {
12 throw new IllegalArgumentException ( "无效的证书" , e ) ;
13 } catch ( IllegalBlockSizeException | BadPaddingException e ) {
14 throw new IllegalBlockSizeException ( "加密原串的长度不能超过214字节" ) ;
15 }
16 } 1 secretMessage : = [ ] byte ( "send reinforcements, we're going to advance" )
2 rng : = rand . Reader
3 cipherdata , err : = EncryptOAEP ( sha1 . New ( ) , rng , rsaPublicKey , secretMessage , nil )
4 if err != nil {
5 fmt . Fprintf ( os . Stderr , "Error from encryption: %s\n" , err )
6 return
7 }
8 ciphertext : = base64 . StdEncoding . EncodeToString ( cipherdata )
9 fmt . Printf ( "Ciphertext: %s\n" , ciphertext ) 1 private
2 function getEncrypt ( $str ) {
3
4 $public_key_path = '平台证书路径' ;
5 $public_key = file_get_contents ( $public_key_path ) ;
6 $encrypted = '' ;
7 if ( openssl_public_encrypt ( $str , $encrypted , $public_key , OPENSSL_PKCS1_OAEP_PADDING ) ) {
8
9 $sign = base64_encode ( $encrypted ) ;
10 } else {
11 throw new Exception ( 'encrypt failed' ) ;
12 }
13 return $sign ;
14 } 1
2
3
4
5
6
7
8
9
10 public static string RSAEncrypt ( string text , byte [ ] publicKey )
11 {
12 using ( var x509 = new X509Certificate2 ( publicKey ) )
13 {
14 using ( var rsa = ( RSACryptoServiceProvider ) x509 . PublicKey . Key )
15 {
16 var buff = rsa . Encrypt ( Encoding . UTF8 . GetBytes ( text ) , true ) ;
17 return Convert . ToBase64String ( buff ) ;
18 }
19 }
20 }