上篇讨论了『计费能力2.0版』的使用,对于第二个API『按次类支付接口』我没有展开,原本以为可以举一反三,但事实上,有个字段signature 却不是那么容易反出来的。
所以,我补充说明一下这个字段值的生成,代码同样参考我放在gitHub上的工程。
A)文档研读
来看这个API,有请求消息中有两个字段需要注意的,一是signType,签名类型,文档要求我们直接填写『HMAC-SHA1』,这是因为WO+平台暂只支持这一种签名算法。第二是signature,文档的描述是:
除signType和signature外,将post中的参数,所有参数按key进行字典升序排列,排序后的参数(key=value)用&拼接起来,生成待签名字符串。
signature= base64_encode(签名算法("sha1",$待签名字符串, $(appKey&appSecret), true))。
还是很好理解,不过有个坑要注意,这里的排序是忽略大小写的。
好吧,我们的方案也出来了,将原来的参数Map转换成TreeMap,然后利用它的排序功能输出并拼装一个字符串(Key1=Value1&Key2=Value2)。再利用J2SE中自带的安全包实现HMAC的签名算法。
B)编写代码
方案有了,代码也就有了。可以参考gitHub中那个工程的EMCSign类。
public static String signValue(HashMap<String,Object> params,String secretKey){
String password=null;
TreeMap<String,Object> tmap = new TreeMap<String,Object>(new Comparator<String>(){
//<span style="color:#009900;">注意,我们Key的排序是忽略大小写的</span>
public int compare(String k1, String k2) {
// TODO Auto-generated method stub
return k1.toLowerCase().compareTo(k2.toLowerCase());
}});
tmap.putAll(params);
try {
StringBuilder sb=new StringBuilder();
for(Object key :tmap.keySet()){
sb.append("&");
sb.append(key);
sb.append("=");
sb.append(tmap.get(key));
}
String signStr = sb.toString();
String source = checkMsgDigest(signStr.substring(1), secretKey);
password = source;
} catch (Exception e) {
e.printStackTrace();
}
return password;
}
上述代码演示了从Map到TreeMap再拼出要签名的字符串的过程。再看它调用的那个方法 checkMsgDigest。
static String CRYPTO_NAME_HMAC_SHA1 = "HmacSHA1";
static byte[] getByte(String source, String secretKey)
throws NoSuchAlgorithmException, InvalidKeyException {
try {
SecretKeySpec secret = new SecretKeySpec(secretKey.getBytes(),
CRYPTO_NAME_HMAC_SHA1);
Mac mac = Mac.getInstance(CRYPTO_NAME_HMAC_SHA1);
mac.init(secret);
byte[] result = mac.doFinal(source.getBytes("UTF-8"));
return result;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
static String checkMsgDigest(String sourceStr,String secretKey) throws InvalidKeyException, NoSuchAlgorithmException {
byte[] msgBase64Digest;
byte[] tmpEncrypted = getByte(sourceStr, secretKey);
msgBase64Digest = Base64.encodeBase64(tmpEncrypted);
String passWord = null;
try {
passWord = new String(msgBase64Digest, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return passWord;
}
这两个方法真没啥可说了,网上现成的。就是用HmacSHA1算法,用appSecret作密钥,对传入的字符串签名,编码为UTF-8。
至此,signature的值也就就绪了。
现在,你可以真正的举一反三了。