一、手册总览
为了构建更安全的安全防控能力,支付宝开放平台正在推动所有使用MD5密钥加/验签的接口进行升级,具体而言,就是从之前MD5加签算法升级到RSA算法,本手册主要指导商户快速完成密钥升级,包含了密钥升级的全流程演示,商户完成该升级大致需要经过2个步骤。
1. 在开放平台上传自己的开发者私钥,下载保存支付宝的公钥;
2. 对商家服务端代码进行升级,将MD5加/验签名逻辑替换成为RSA签名逻辑
PS:如果您是使用了第三方服务提供商提供的软件/系统,请联系服务提供商完成升级!
二、RSA密钥查看/修改
2.1 进入密钥管理功能页
访问开放平台管理中心:https://openhome.alipay.com/platform/developerIndex.htm,进入【密钥管理】功能页
2.2 设置开发者公钥
因为RSA密钥是非对称密钥,开发者生成RSA密钥后,自己保存好私钥,将公钥上传到开放平台。在进行接口参数加签时,使用自己RSA私钥进行签名,支付宝侧将使用用户上传的RSA公钥进行请求验签,确保请求在传输中未被篡改。
密钥生成方法:https://opendocs.alipay.com/mini/02c7i5
2.3 查看支付宝公钥
支付宝侧为每个商户生成了不同的RSA密钥,其中开发者可以在密钥管理中心查看支付宝公钥,支付宝私钥由支付宝开放平台安全保存,不对外泄漏。商家在接收到支付宝侧的服务通知时,需要使用支付宝公钥进行请求验签,确保该请求在传输中未被篡改。
三、商家服务端升级
如果加/验签名逻辑是商户自定义开发,支付宝使用的是标准的加/验签名逻辑,商户只需可以参照代码demo完成升级即可;
a) 接口参数加签逻辑介绍
详见:https://opendocs.alipay.com/open/58/103591
b) 支付通知验签逻辑介绍
签名验证:https://opendocs.alipay.com/open/58/103596
3.1 签名代码Demo
a) 获取待加签字符串
如将参数存储在Map中,获取待签名参数的代码Demo如下所示:
/**
* 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
* @param params 需要排序并参与字符拼接的参数组
* @return 拼接后字符串
*/
public static String createLinkString(Map<String, String> params) {
List<String> keys = new ArrayList<String>(params.keySet());
Collections.sort(keys); //按照参数key有序排列
String prestr = "";
for (int i = 0; i < keys.size(); i++) { //有序合并参数
String key = keys.get(i);
String value = params.get(key);
if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&";
}
}
return prestr;
}
b) 请求加签
获得有序的待加签名字符后,即可使用标准的RSA加签算法对请求进行加签名,如下Demo中使用的是java.security.Signature类完成请求加签。
/**
* 生成签名结果
* @param sPara 要签名的参数Map
* @return 签名结果字符串
*/
public static String buildRequestMysign(Map<String, String> sPara) {
String prestr = AlipayCore.createLinkString(sPara); //把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
String mysign = "";
if(AlipayConfig.sign_type.equals("RSA") ){
mysign = RSA.sign(prestr, AlipayConfig.private_key, AlipayConfig.input_charset);
}
return mysign;
}
-----------------------分割线-----------------------------
/**
* RSA签名
* @param content 待签名数据
* @param privateKey 商户私钥
* @param input_charset 编码格式
* @return 签名值
*/
public static String sign(String content, String privateKey, String input_charset)
{
try
{
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec( Base64.decode(privateKey) );
KeyFactory keyf = KeyFactory.getInstance("RSA");
PrivateKey priKey = keyf.generatePrivate(priPKCS8);
//获取SHA1WithRSA签名算法对象
java.security.Signature signature = java.security.Signature.getInstance("SHA1WithRSA");
signature.initSign(priKey); //初始化私钥
signature.update( content.getBytes(input_charset) ); //设置需要签名的数据
byte[] signed = signature.sign(); //获得签名值(bytes类型)
return Base64.encode(signed); //签名值转换成为base64格式(String类型)
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
c) 合并请求参数
为了支付宝侧能够验证签名,签名值和采用的签名算法,需要与请求参数合并,一起发送出来。
/**
* 生成要请求给支付宝的参数数组
* @param sParaTemp 请求前的参数数组
* @return 要请求的参数数组
*/
public static Map<String, String> buildRequestPara(Map<String, String> sParaTemp) {
//除去数组中的空值和签名参数
Map<String, String> sPara = AlipayCore.paraFilter(sParaTemp);
//生成签名结果
String mysign = buildRequestMysign(sPara); //获取参数签名结果
//签名结果与签名方式加入请求提交参数组中
sPara.put("sign", mysign); //参数签名值
sPara.put("sign_type", AlipayConfig.sign_type); //采用的签名算法类型
return sPara;
}
3.2 验证签名Demo
a) 获取待验签字符
同“签名代码Demo/获取待加签字符串”的逻辑完全相同,请直接参考;
b) 验证签名
获取待验签字符串后,同样可以调用标准的RSA的验签算法进行验签,Demo中同样采用的是java.security.Signature类完成请求验签。
/**
* RSA验签名检查
* @param content 待签名数据
* @param sign 签名值
* @param alipay_public_key 支付宝公钥
* @param input_charset 编码格式
* @return 布尔值
*/
public static boolean verify(String content, String sign, String alipay_public_key, String input_charset)
{
try
{
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] encodedKey = Base64.decode(alipay_public_key);
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
java.security.Signature signature = java.security.Signature
.getInstance("SHA1WithRSA");
signature.initVerify(pubKey); //初始化公钥
signature.update( content.getBytes(input_charset) ); //设置待验签字符
boolean bverify = signature.verify( Base64.decode(sign) ); //验证签名是否一致
return bverify;
}
catch (Exception e)
{
e.printStackTrace();
}
return false;
}
3.3 官方SDK Demo:
为了帮助开发者快速完成升级,也为了开发者通过代码快速熟悉RSA加/验签名流程,支付宝提供了【官方DEMO】,开发者可以参考官方demo的【RSA签名版本】代码逻辑,了解加/验签逻辑,并完成RSA加/验签流程改造。
官方SDK DEMO:https://opendocs.alipay.com/open/66/104511
四、FAQ问题汇总
a) 接口加签算法升级为RSA后, 相应服务通知验签算法也要升级成为RSA?
答:是的,由于接口参数加签算法与支付宝侧服务通知的加签算法不一定严格对应(接口参数使用RSA签名,接口通知不一定使用RSA),建议商户在完成接口加签升级后,对服务通知的验签逻辑也进行升级,使服务端通知同时支持MD5和RSA算法;具体而言,接口加签只支持RSA,服务通知验签同时支持MD5和RSA。
b) 为什么要强制MD5密钥升级到RSA密钥?
答:随着计算机算力的提升,MD5密钥已经无法应对未来的安全要求,并且国家监管部门已经出台相关的制度法规,为了您业务安全,也为了业务合规考虑,请您尽快升级。
c) MD5密钥升级时长是多少?
从通知触达之日起,为期6个月,请商户尽量在期限内完成升级!逾期未升级商户,支付宝将根据商户的升级情况,评估是否采用进一步的安全措施。
五、整改风险提示
商户在进行RSA密钥升级时,一定要注意加签RSA私钥安全存放,禁止将RSA私钥文件直接保存在服务端程序的文件目录中,避免被外部访问,造成RSA私钥泄漏;