一、支付宝开放平台的公私钥机制
支付宝开放平台的应用管理体系,使用了公私钥的机制,来保障商户应用和支付宝交互的安全性。这一机制包括两个部分:
- 商户应用使用自己的私钥对消息加签之后,将消息和签名传递给支付宝,支付宝则使用应用的公钥验证消息的真实性(来自于合法应用的真实消息)。
- 对于支付宝返回消息给商户应用的情形,应用则使用支付宝的“平台公钥”来验证返回消息的真实性。
下图简单描述了上述第一部分的交互机制:
这一机制的关键前提假定是:
商户应用必须保障应用私钥的安全性,从而保障应用和和支付宝交互的安全性。
反之,一旦私钥发生泄露,商户则会面临较大的安全风险。若应用和支付宝的交互涉及到资金类接口则风险更甚。
二、阿里云KMS带来的价值
相比于在应用中使用应用私钥的明文来对消息进行加签,阿里云KMS为用户带来了如下几个方面的优势:
- 保障私钥安全性:用户可以将签名私钥安全托管在KMS的托管密码机内。用户通过KMS的开放API使用私钥加签时,私钥会在密码机的硬件安全边界之内,完成运算后返回签名值,从而防止私钥的泄露。
- 控制谁可以使用私钥:用户可以通过阿里云访问控制服务(RAM服务),中心化的管理和控制谁可以使用KMS中的密钥,用于应用加签。
- 审计对私钥的调用日志:通过阿里云操作审计服务可以查看每一次调用KMS的记录;而商家应用自行保管私钥则很难产生“客观”的审计事件。
- 对安全事件的响应能力:在应用系统遭遇恶意者攻击等安全事件的情形下,也可以通过多种手段阻止对私钥的非法使用:例如用户可以通过RAM服务撤销对私钥的使用权限,或者通过KMS禁用私钥等。
三、使用阿里云KMS配置应用密钥
第一步:在阿里云KMS中创建应用密钥
- 登录阿里云KMS的管理控制台。
- 在左上角的地域选择栏,选择合适的地域。通常情况下,建议您选择和应用部署相同的KMS地域创建密钥。
-
点击 创建密钥,选择密钥类型和密钥用途。
- 密钥类型使用 RSA_2048。
- 密钥用途选择 Sign/Verify。
- 保护级别选择 Hsm,即通过KMS系统的硬件加密机产生和保护密钥
第二步:在支付宝配置应用密钥
支付宝开放平台提供了两种配置方法,普通公钥方式和公钥证书方式。
公钥证书方式是对普通公钥方式的增强机制,从数字签名的角度来看,机制大同小异,商户应用只需要二选一即可。注意:对于涉及到资金往来的商户应用,应当使用公钥证书的方式。
方法一:普通公钥方式
以下步骤参考支付宝开放平台普通公钥方式,并做恰当修改:
- 在阿里云KMS的管理控制台,选择之前创建的RSA_2048类型的密钥,进入详情页之后,点击查看公钥可以复制或者下载应用公钥。
- 进入 支付宝开放平台 并打开对应的应用,在应用的开发配置页面进行 接口加签方式 设置。点击 设置 后,输入手机验证码。
- 复制上一步生成的公钥,粘贴到 填写公钥字符 对应的位置,点击 保存设置,即可完成公钥的设置。
普通公钥方式的一句话总结:
从阿里云KMS获取应用公钥,注册到支付宝开放平台对应的应用中。
方法二:公钥证书方式
以下步骤参考支付宝开放平台公钥证书方式,并做恰当修改:
- 在阿里云KMS的管理控制台,选择之前创建的RSA_2048类型的密钥,进入详情页之后,点击 生成CSR。注意:填写证书属性时,企业/单位名称 一定要和开发者中心门户账号信息的公司名称保持一致,否则会导致后续步骤中上传 CSR 证书文件校验失败。
- 进入 支付宝开放平台 并打开对应的应用,在应用的开发配置页面进行 接口加签方式 设置。点击 设置 后,输入手机验证码。
-
加签模式 选择 公钥证书 ,上传证书文件 选择 上传CSR文件在线生成证书。最后点击 上传CSR文件在线生成证书,即可完成公钥证书的设置。
公钥证书方式的一句话总结:
从阿里云KMS获取应用私钥的证书请求 CSR,到支付宝开放平台一键完成应用证书签发和注册。
四、应用中调用阿里云KMS
支付宝开放平台新版SDK,也就是EasySDK集成了阿里云KMS作为加签提供器(Sign Provider),简化使用。以Java为例,您需要在应用中引用EasySDK 2.0.1以及之后的版本。
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-easysdk</artifactId>
<version>2.0.1</version>
</dependency>
当然,如果遵循支付宝开放API的签名规则,也可以不使用EasySDK,通过阿里云KMS的AsymmetricSign接口自行实现签名。
代码示例:普通公钥方式
本示例来自于阿里云KMS的Github代码样例库。
package com.aliyun.kms.samples;
import com.alipay.easysdk.base.qrcode.models.AlipayOpenAppQrcodeCreateResponse;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kms.aliyun.AliyunKMSConfig;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Map;
/**
* Alipay-easysdk使用KMS签名示例,本示例展示了公钥模式调用
*/
public class KmsAlipayEasySDKPublicKeyDemo {
public static void main(String[] args) {
Factory.setOptions(getOptions());
try {
AlipayOpenAppQrcodeCreateResponse response = Factory.Base.Qrcode().create("page/component/component-pages/view/view", "x=1", "二维码描述");
if ("10000".equals(response.code)) {
System.out.println("调用成功");
} else {
System.err.println("调用失败,原因:" + response.msg + "," + response.subMsg);
}
} catch (Exception e) {
System.err.println("调用遭遇异常,原因:" + e.getMessage());
throw new RuntimeException(e.getMessage(), e);
}
}
private static AliyunKMSConfig getOptions() {
AliyunKMSConfig config = new AliyunKMSConfig();
config.protocol = "https";
config.gatewayHost = "openapi.alipay.com";
config.signType = "RSA2";
// 请更换为您的AppId
config.appId = "202100****";
// 请修改如下的支付宝公钥字符串为自己的支付宝公钥
config.alipayPublicKey = "MIIBIjANB...";
// 如果使用阿里云KMS签名,则需要指定签名提供方名称,阿里云KMS的名称为"AliyunKMS"
config.signProvider = "AliyunKMS";
// 如果使用阿里云KMS签名,请更换为您的阿里云AccessKeyId
config.aliyunAccessKeyId = getAliyunAccessKey("AccessKeyId");
// 如果使用阿里云KMS签名,请更换为您的阿里云AccessKeySecret
config.aliyunAccessKeySecret = getAliyunAccessKey("AccessKeySecret");
// 如果使用阿里云KMS签名,请更换为您的KMS服务密钥ID
config.kmsKeyId = "4358f298-8e30-4849-9791-****";
// 如果使用阿里云KMS签名,请更换为您的KMS服务密钥版本ID
config.kmsKeyVersionId = "e71daa69-c321-4014-b0c4-****";
// 如果使用阿里云KMS签名,需要更换为您的KMS服务地址
// KMS服务地址列表详情,请参考:
// https://help.aliyun.com/document_detail/69006.html?spm=a2c4g.11186623.2.9.783f77cfAoNhY6#concept-69006-zh
config.kmsEndpoint = "kms.cn-hangzhou.aliyuncs.com";
return config;
}
/**
* 从文件中读取阿里云AccessKey配置信息
* 此处为了测试执行的环境普适性,AccessKey信息配置在resources资源下,实际过程中请不要这样做。
*
* @param key AccessKey配置对应的key
* @return AccessKey配置字符串
*/
private static String getAliyunAccessKey(String key) {
InputStream stream = KmsAlipayEasySDKPublicKeyDemo.class.getResourceAsStream("/fixture/aliyunAccessKey.json");
Map<String, String> result = new Gson().fromJson(new InputStreamReader(stream), new TypeToken<Map<String, String>>() {
}.getType());
return result.get(key);
}
}
代码示例:公钥证书方式
使用公钥证书模式时,EasySDK的使用方式和上述示例类似,区别主要在于配置了商户应用和支付宝平台的公钥证书。
请参考阿里云KMS的Github代码样例库了解更多。