3.4安全码
计算安全码使用的算法 SHA1
3.4.1安全码原文
将所有“名值对”和APIKey拼接
名值对 | APIKey |
---|---|
key1=value1&key2=value2…每一个值从a到z的顺序排序,若遇到相同首字母,则看第二个字母,以此类推 | AppId对应的APIKey值,Base64值 |
比如名值对字母升序之后 a=value1&b=value2&c=value3;APIKey是QVBJS2V5,安全码原文=
a=value1&b=value2&c=value3QVBJS2V5
3.4.2注意
- 计算之前需要将原文以UTF-8字符集编码为字节数组;
- 根据HTTP协议要求,传递参数的值中如果含有特殊字符(如@、+、/),该值需要做URL Encoding,这样服务中心才能接收到正确的参数值;
- 不管是否含有特殊字符,安全码原文使用原始值。
3.4.3请求参数安全码原文格式
安全码原文包含(1)业务参数、(2)除安全码之外平台接入参数、(3)APIKey三部分。比如 “根据票据代码、票号、随机码”接口的安全码原文:
举例
appId
=XXX&dataType=XXX&eInvoiceCode=XXX&eInvoiceNumber=XXX&randomNumber=XXX&serviceName
=XXXYYY
dataType=XXX&eInvoiceCode=XXX&eInvoiceNumber=XXX&randomNumber=XXX表示业务参数
appId
和serviceName
表示平台参数
YYY表示APIKey,原文末尾
3.4.4返回值安全码原文格式
安全码原文中包含status、message、data、APIKey四部分
- 比如 “根据票据代码、票号、随机码”接口失败时安全码原文:
APIKey=XXX&message
=message原文&status
=status原文 - 比如 “根据票据代码、票号、随机码”接口成功时安全码原文:
APIKey=XXX[&data
=data原文]&message
=message原文&status
=status原文
代码参考
- 拼接
public static void main(String[] args) throws Exception {
Map<String, String>params = new HashMap<>();
params.put("appId", "XXX");
params.put("serviceName", "checkAccount");
params.put("dataType", "json");
String builderSignStr = builderSignStr(params);
String apiKey = "APIKey";
byte[] securityCode= EncryptUtils.hmac(apiKey, builderSignStr + apiKey);
// 转化securityCode 为 base64 字符串
System.out.println(securityCode);
}
public static String builderSignStr(Map<String, String> params) {
Set<String> keySet = params.keySet();
List<String>keyList = new ArrayList<String>(keySet);
Collections.sort(keyList);
StringBuilder sb = new StringBuilder();
for (String key : keyList) {
sb.append(key);
sb.append("=");
sb.append(params.get(key));
sb.append("&");
}
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
public static String md5(String plaintext) {
assert plaintext != null : "plaintext may arg not be null";
byte[] bytes = null;
String encoded = "";
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.reset();
digest.update(plaintext.getBytes(Charset.forName("utf-8")));
bytes = digest.digest();
encoded = Base64.encodeBytes(bytes);
} catch (NoSuchAlgorithmException e) {
//throw new EncryptException(e.getMessage(), e);
}
return encoded;
}
- 每一个值从a到z的顺序排序,若遇到相同首字母,则看第二个字母,以此类推
public static void main(String[] args) throws Exception {
java.util.SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("status", "{merBillNo:2015090700000000}");
packageParams.put("message", "2015090700000000");
packageParams.put("data", "2015090700000000");
String str = new SortedMap().createSign(packageParams);
System.out.println(str);
String str2 = new SortedMap().createSign2(packageParams);
System.out.println(str2);
}
/**
* 规则是:按参数名称a-z排序,遇到空值的参数不参加签名。V型知识库www.vxzsk.com
*/
public String createSign(java.util.SortedMap<String, String> packageParams) throws Exception {
StringBuffer sb = new StringBuffer();
Set<?> es = packageParams.entrySet();
Iterator<?> it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
return sb.toString();
}
/**
* 规则是:按参数名称a-z排序,遇到空值的参数不参加签名。V型知识库www.vxzsk.com
*/
public String createSign2(java.util.SortedMap<String, String> packageParams) throws Exception {
StringBuffer sb = new StringBuffer();
if (packageParams != null) {
for (Map.Entry entry : packageParams.entrySet()) {
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
}
sb.append("key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
return sb.toString();
}