签名与验签
本文介绍如何在半个小时之内生成调用到位平台接口的签名,及签名加密规则,实现轻松调用到位平台的相关接口。
一、签名计算基本设定
- 到位平台的所有接口中的所有参数的编码都是UTF-8,所以跟到位平台交互或者计算签名的时候字符编码都是UTF-8,暂时没有支持其他编码的打算,请开发者自行对于自己的代码编码进行转换。
- 请求接口的参数列表均为键值对,键名为字符串,键值也必须为字符串。
- 键值中如果为复杂数据类型,比如结构体、数组、对象都必须先转化成为字符串。
二、筛选进行签名的参数
参数名为rsaSign不需要参与签名。
三、对参数进行排序
将筛选的参数按照第一个字符的键值ASCII码递增排序(字母升序排序),如果遇到相同字符则按照第二个字符的键值ASCII码递增排序,以此类推。
四、对参数进行拼接组成待签名字符串
- 使用 = 和 & 将参数连接起来,将排序后的参数与其对应值,组合成“参数=参数值”的格式,并且把这些参数用&字符连接起来,此时生成的字符串为待签名字符串。
amount=100&orderId=2017011215064442155179691603&serviceId=304f5ea4f3a74eec8e2cd7ff0b668628&userId=e285290a152f4e05a71058c48899b622
- 使用各自语言对应的SHA1WithRSA签名生成函数(如php: openssl_sign),传入待签名字符串、业务方私钥,由SHA1算法中得出sign,然后base64encode。示例生成的签名为:
rsaSign=AxDNwRHnyqgaJ/r7vApS4cw0kqwEqyZMyhfIua9kg7RrZhtCyrtLxc2U0pRV6HZBBA6y17AreOWxdrGbJX2Nj9r6mLrCXPXk1ZCLvs+laK561K4Ssh7sgTzE72NerbAQQwThhyI6Fxy6oISNS29HjUzb52nMfQuYqb7+tpN0UaU=
五、JAVA相关的签名生成的参考代码
1、JAVA生成签名的工具类
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
public class SignatureUtils {
/**
* 生成签名
*
* @param sortedParams
* @param privateKey
* @return
*/
public static String genSign(Map<String, String> sortedParams, String privateKey) {
String sortedParamsContent = getSignContent(sortedParams);
return rsaSign(sortedParamsContent, privateKey, "UTF-8");
}
/**
* 验证签名
*
* @param param
* @param publicKey
* @return
*/
public static boolean checkSign(Map<String, String> param, String publicKey) {
if (param == null || param.isEmpty()) {
return false;
}
String sign = param.get("sign");
if (StringUtils.isEmpty(sign)) {
return false;
}
if (param.containsKey("sign")) {
param.remove("sign");
}
String content = getSignContent(param);
return doCheck(content, sign, publicKey);
}
/**
*
* @param sortedParams
* @return
*/
public static String getSignContent(Map<String, String> sortedParams) {
StringBuffer content = new StringBuffer();
List<String> keys = new ArrayList<String>(sortedParams.keySet());
Collections.sort(keys);
int index = 0;
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = sortedParams.get(key);
if (StringUtils.isNotEmpty(key) && StringUtils.isNotEmpty(value)) {
content.append((index == 0 ? "" : "&") + key + "=" + value);
index++;
}
}
return content.toString();
}
/**
* sha1WithRsa 加签
* @param content
* @param privateKey
* @param charset
* @return
* @throws DaowayApiException
*/
public static String rsaSign(String content, String privateKey, String charset) {
try {
PrivateKey priKey = getPrivateKeyFromPKCS8("RSA", new ByteArrayInputStream(privateKey.getBytes()));
java.security.Signature signature = java.security.Signature.getInstance("SHA1WithRSA");
signature.initSign(priKey);
if (StringUtils.isEmpty(charset)) {
signature.update(content.getBytes());
} else {
signature.update(content.getBytes(charset));
}
byte[] signed = signature.sign();
return new String(Base64.encodeBase64(signed));
} catch (Exception e) {
System.out.println(e);
return null;
}
}
public static boolean doCheck(String content, String sign, String publicKey) {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] encodedKey = Base64.decodeBase64(publicKey);
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
java.security.Signature signature = java.security.Signature.getInstance("SHA1WithRSA");
signature.initVerify(pubKey);
signature.update(content.getBytes("UTF-8"));
boolean bverify = signature.verify(Base64.decodeBase64(sign));
return bverify;
} catch (Exception e) {
return false;
}
}
public static PrivateKey getPrivateKeyFromPKCS8(String algorithm, InputStream ins) throws Exception {
if (ins == null || StringUtils.isEmpty(algorithm)) {
return null;
}
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
byte[] encodedKey = IOUtils.toByteArray(ins);
encodedKey = Base64.decodeBase64(encodedKey);
return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
}
}
2、调用JAVA签名生成类生成签名的参考代码
/**
* 第一部分:赋值私钥
* @notice1:因为JAVA中的字节流、字符流、双字符流等概念非常复杂,建议使用常量存储私钥文件
* @notice2:私钥文件为不换行且不带私钥开头和结尾的字符串
*/
String PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAM8vojS3eWYEnB0Xl73+8+D/xdzeWTvbZc0SPO6nKmm3WxBYX/fFI6S7DhhK0QAUKjbSD4hDyqLkgy8azi8ETvYSYIoTjdR55nNklCNQ9RAtPVeuAAFzN0h2DmfY3/F7FsUFg9Qd/9YaGpU+CEnZDThjvWxBg22HvlN09xKfAYltAgMBAAECgYAr8wDHifv4hhXPngeUSBbXran9NjVbmyi3HZ1LSq6WikyI5RZGas0qznso8AXxrFVgF6Mv1qGPeEXToi4GjzVoX5ocfUoSlqE5xmhdfmc4aqKz/BlncCVlgNnlQEp5oHpGiIzVEpabC4OiBMRAhi/Brvu14GOUkP1VEZmfuCQCCQJBAPKytmmzznsDaiO15AeorPi/nUNDMLoOoiFwZgUxXWW7PI+uZq1ja5NpMjuRu3eVt3dFexB7x+ZnBb9tWTGQtDMCQQDaiqc4vR1eiSpVMf+rB6+Xbj+dDrtoTaH66YrBKXE5tbWPlsm1MWWpmDREFntU+f3yAQqjgVAtCULmp8odkCvfAkEAge9aJ+dDIarnVW0ZQ1x0Fs0Hli5P1Rzmgn6ZsCgIt+Fxf/9AK44x1v8YDLpuIoz+Z5XEWEPc9yaq9hzGBvpQ7wJAErDLDnI2IdCvWyv0hscYgGYAcMlCw+/ny5LPuCd4NIxS493skF+SJ0gKKEyX7bOXwWvPYh58Ie3p19o/0flzlwJBAJ8Ut/aPdzIFIlvR8BdQ7O/6BCf2490vWjNrzu+TOWCEeEM4IMfgXSg3chhExJg8TXwU0IbiB5fnDeIreWbPPWY=";
/**
* 第二部分:计算签名
*/
/**
* 1.通过方法获取到所有需要需要参与签名的参数HashMap
*/
HashMap params = new HashMap();
/**
* 2.从常量中读取privateKey,然后计算RSA签名
*/
String rsaSign = SignatureUtils.genSign(params,PRIVATE_KEY);