• 下载
  • 社区

内容加密接入指引

安全设置

AES密钥获取

进入小程序详情页 > 开发设置 > 内容加密方式,首次设置可以看到 设置 按钮(若已设置则为 查看 按钮),经过核身校验后可以查看 AES 设置页面(该密钥由支付宝开放平台生成,开发者仅需要将密钥配置到你的系统中即可)。


注意:若已配置密钥,请谨慎重新重新生成密钥,否则若你的服务端密钥没有更新最新密钥会导致解密失败。


获取支付宝公钥

在小程序详情页=>设置=>开发设置页面,按照下图操作查看支付宝RSA2公钥(若看不到“查看支付宝公钥”入口,请按照“设置应用公钥”文档设置应用的RSA2公钥)



敏感接口服务端处理逻辑

注意:本文档仅说明jsApi的加解密处理逻辑,openapi接口的AES加密功能请参考openapi加解密使用

在涉及敏感数据的情况下,jsApi返回的结果是密文,同时会对报文进行签名,需要传递到开发者服务端进行报文解密,整体的交互流程如下图所示:


完整报文示例

涉及到开发者服务端处理的安全操作包括:解密和验签。由于是否加密取决于数据的安全的敏感程度,所以存在明文的情况。


示例1(涉及解密)

res.response为完整的报文数据,示例如下(为了展示方便,报文示例均作了json的美化处理)

{
  "response": "hvDOnibG0DPcOFPNubK3DEfLQGL4=",
  "sign": "OIwk7zfZMp5GX78Ow==",
  "sign_type": "RSA2",
  "encrypt_type": "AES",
  "charset": "UTF-8"
}


其中,各字段说明如下

字段名

字段说明

必填

response

报文(密文)

sign

对response报文的签名

sign_type

加签算法

否(应用维度配置的加签算法,小程序默认为RSA2)

encrypt_type

加密算法

否(默认为AES)

charset

验签和解密用的字符集

否(默认为UTF-8)

示例2(不涉及解密)

res.response为完整的报文数据,示例如下(为了展示方便,报文示例均作了json的美化处理)

{
  "response": {
    "code": "10000",
    "msg": "Success",
    "userName": "小明"
  },
  "sign": "OIwk7zfZMp5GX78Ow==",
  "sign_type": "RSA2",
  "charset": "UTF-8"
}


其中,各字段说明如下

字段名

字段说明

必填

response

报文

sign

对response报文的签名

sign_type

加签算法

否(应用维度配置的加签算法,小程序默认为RSA2)

charset

验签用的字符集

否(默认为UTF-8)


验签与解密处理

无论是否涉及解密,Java后端处理逻辑都可以按照下述逻辑处理(以下代码仅作为代码示例,生产环境使用请注意异常处理逻辑;其它语言请参考常见问题中验签解密通用逻辑的说明)

String response = "小程序前端提交的";

//1. 获取验签和解密所需要的参数
Map<String, String> openapiResult = JSON.parseObject(response,
            new TypeReference<Map<String, String>>() {
            }, Feature.OrderedField);
String signType = StringUtils.defaultIfBlank(openapiResult.get("signType"), "RSA2");
String charset = StringUtils.defaultIfBlank(openapiResult.get("charset"), "UTF-8");
String encryptType = StringUtils.defaultIfBlank(openapiResult.get("encryptType"), "AES");
String sign = openapiResult.get("sign");
String content = openapiResult.get("response");

//如果密文的
boolean isDataEncrypted = !content.startsWith("{");
boolean signCheckPass = false;

//2. 验签
String signContent = content;
String signVeriKey = "你的小程序对应的支付宝公钥(为扩展考虑建议用appId+signType做密钥存储隔离)";
String decryptKey = "你的小程序对应的加解密密钥(为扩展考虑建议用appId+encryptType做密钥存储隔离)"
//如果是加密的报文则需要在密文的前后添加双引号
if (isDataEncrypted) {
    signContent = "\"" + signContent + "\"";
}
try {
    signCheckPass = AlipaySignature.rsaCheck(signContent, sign, signVeriKey, charset, signType);
} catch (AlipayApiException e) {
    //验签异常, 日志
}
if(!signCheckPass) {
    //验签不通过(异常或者报文被篡改),终止流程(不需要做解密)
    throw new Exception("验签失败");
}

//3. 解密
String plainData = null;
if (isDataEncrypted) {
    try {
        AlipayEncrypt.decryptContent(content, encryptType, decryptKey, charset);
    } catch (AlipayApiException e) {
        //解密异常, 记录日志
       throw new Exception("解密异常");
    }
} else {
    plainData = content;
}


  • AlipaySignature、AlipayEncrypt使用的是 开放平台服务端sdk

  • 示例代码中json处理采用fastjson处理。注意这里一定要使用Feature.OrderedField,否则会出现参数顺序问题导致验签失败


C#后端处理逻辑示例代码如下


string response = "小程序前端提交的";

//1. 获取验签和解密所需要的参数
IDictionary openapiResult = Jayrock.Json.Conversion.JsonConvert.Import(response) as IDictionary;

string signType;
if (openapiResult.Contains("sign_type"))
{
    signType = openapiResult["sign_type"].ToString();
}
else
{
    signType = "RSA2";
}

string charset;
if (openapiResult.Contains("charset"))
{
    charset = openapiResult["charset"].ToString();
}
else
{
    charset = "UTF-8";
}

string encryptType;
if (openapiResult.Contains("encrypt_type"))
{
    encryptType = openapiResult["encrypt_type"].ToString();
}
else
{
    encryptType = "AES";
}

string sign = openapiResult["sign"]..ToString();
string content = openapiResult["response"].ToString();

//如果密文的
bool isDataEncrypted = !content.StartsWith("{", StringComparison.Ordinal);
bool signCheckPass = false;

//2. 验签
string signContent = content;
string signVeriKey = "你的小程序对应的支付宝公钥(为扩展考虑建议用appId+signType做密钥存储隔离)";
string decryptKey = "你的小程序对应的加解密密钥(为扩展考虑建议用appId+encryptType做密钥存储隔离)";
//如果是加密的报文则需要在密文的前后添加双引号
if (isDataEncrypted)
{
    signContent = "\"" + signContent + "\"";
}
try
{
    signCheckPass = AlipaySignature.RSACheckContent(signContent, sign, signVeriKey, charset, signType);
}
catch (Exception ex)
{
    //验签异常, 日志
    throw new Exception("验签失败", ex);
}
if (!signCheckPass)
{
    //验签不通过(异常或者报文被篡改),终止流程(不需要做解密)
    throw new Exception("验签失败");
}

//3. 解密
string plainData = null;
if (isDataEncrypted)
{
    try
    {
        plainData = AlipayEncrypt.AesDencrypt(decryptKey, content, charset);
    }
    catch (Exception ex)
    {
        //解密异常, 记录日志
        throw new Exception("解密异常", ex);
    }
}
else
{
    plainData = content;
}
  • AlipaySignature、AlipayEncrypt使用的是 开放平台服务端sdk
  • Jayrock.Json.Conversion.JsonConvert使用的是jayrock-json库中的工具类。


常见问题

AES密钥是什么

高级加密标准(Advanced Encryption Standard,AES),是目前对称密钥加密中比较通用的一种加密方式。


开放平台AES密钥有什么用

更安全。加密后,在网络上传输的接口报文内容将会由明文内容变为密文内容,可以大大提升接口内容传输的安全性。支付宝开放平台所有OpenAPI均支持对接口的请求内容和响应内容进行AES加密;另外,部分jsApi接口也支持AES加密。


AES密钥与RSA密钥的关系

  • AES密钥是对接口请求和响应内容进行加密,密文无法被第三方识别,从而防止接口传输数据泄露。

  • RSA密钥是对接口请求和响应内容进行签名,开发者和支付宝开放平台分别加签验签,以确认接口传输的内容没有被篡改。不论接口内容是明文还是密文,RSA均可正常签名。

  • 开发者可对请求参数先做AES加密,然后对密文进行RSA签名。


openapi接口是否支持AES加密

支持。openapi接口的AES加密功能请参考openapi加解密使用。注意,openapi的接口加解密功能不限制应用类型,也就是说非小程序应用也支持。


验签失败

验签失败的可能情况主要包括以下几类,请按顺序依次排查

  • 支付宝公钥是否正确

请根据前文所述方法查看支付宝公钥

  • 字符集是否正确

返回报文中若有charset则以该参数指定的为准,若没有则默认为UTF-8

  • 签名算法是否正确

返回报文中若有signType则以该参数指定的为准,若没有则默认为RSA2


解密失败

解密失败的原因可能情况主要包括以下几类,请按顺序依次排查

  • AES密钥是否正确

请根据前文所述方法查看AES密钥

  • 字符集是否正确

返回报文中若有charset则以该参数指定的为准,若没有则默认为UTF-8

  • 加密算法是否正确

返回报文中若有encryptType则以该参数指定的为准,若没有默认为AES


验签解密通用逻辑

若你采用其它语言实现验签和解密,则可以根据该语言特性自行实现,通用的处理逻辑描述如下:


判断报文是否加密

若报文没有加密,则response的值为json,而加密情况response是Base64字符串。所以可以通过response的值是否是括号符开头:“{”来进行判断


获取验签原文

正常情况response的值即为验签原文。但是若为加密报文,则需要加上前后的双引号。以下高亮部分即为加密和非加密报文情况下的验签原文:


{
 "response": "hvDOnibG0DPcOFPNubK3DEfLQGL4=",
 "sign": "OIwk7zfZMp5GX78Ow==",
 "sign_type": "RSA2",
 "encrypt_type": "AES",
 "charset": "UTF-8"
}

{
 "response": {
   "code": "10000",
   "msg": "Success",
   "userName": "小明"
 },

 "sign": "OIwk7zfZMp5GX78Ow==",
 "sign_type": "RSA2",
 "charset": "UTF-8"
}


获取签名配置

签名配置优先采用支付宝报文里申明的配置,若没有则采用默认配置(所以处理逻辑中会有类似StringUtil.defaultIfBlank(openapiResult.get("signType"), "RSA2")这样的处理)。默认配置值如下:

签名算法

RSA2

加密算法

AES

验签字符集

UTF-8

验签

可以基于自己的语言实现验签逻辑,也可以参考服务端SDK的相应语言的底层验签方法,例如:

  • PHP

//php和Java针对字符集的处理方式不一样。php在验签之前需要通过iconv先将待验签字符转为验签字符集指定的字符集
function verify($data, $sign, $rsaPublicKeyFilePath, $signType = 'RSA2')
  • C#

public static bool RSACheckContent(string signContent, string sign, string publicKeyPem, string charset, string signType)


解密

可以基于自己的语言实现解密逻辑,也可以参考服务端SDK的相应语言的底层解密方法,例如:

  • PHP

//参考AopEncrypt.php的以下方法(由于密文是base64字符串,均为ASCII字符,所以不存在字符集问题)
function decrypt($str,$screct_key)


  • C#

//参考AlipayEncrypt.cs文件的以下方法
public static string AesDencrypt(string encryptKey, string bizContent, string charset)