更新时间:2019-11-20
1.待请求数据:
CanonicalRequest = HTTPRequestMethod + '\n' + CanonicalURI + '\n' + CanonicalHeaders + '\n' + HexEncode(Hash(RequestPayload))
HTTPRequestMethod:http请求方法
CanonicalURI:URL路径,计算签名时,URI必须以“/”结尾。发送请求时,可以不以“/”结尾。
CanonicalHeaders:由多个请求消息头(目前协商取content-type\date头域)共同组成,CanonicalHeadersEntry0 + CanonicalHeadersEntry1 + ...,
其中每个请求消息头(CanonicalHeadersEntry )的格式为Lowercase(HeaderName) + ':' + Trimall(HeaderValue) + '\n‘
说明:
Lowercase表示将所有字符转换为小写字母的函数。
Trimall表示删除值前后的多余空格的函数。
最后一个请求消息头也会携带一个换行符。叠加规范中CanonicalHeaders自身携带的换行符,因此会出现一个空行。
请求消息体:
消息体需要做两层转换:HexEncode(Hash(RequestPayload)),其中Hash表示生成消息摘要的函数,当前支持SHA-256算法。
HexEncode表示以小写字母形式返回摘要的Base-16编码的函数。
2.对于构造好的待请求数据再进行哈希处理,算法与对RequestPayload哈希处理的算法相同
HashedCanonicalRequest = Lowercase(HexEncode(Hash.SHA256(CanonicalRequest)))
3.创建待签字符串
StringToSign = Algorithm + \n + RequestDateTime + \n + HashedCanonicalRequest
Algorithm签名算法。算法为HMAC-SHA256。
RequestDateTime请求时间戳。与请求消息头Date的值相同,UTC时间格式为YYYYMMDDTHHMMSSZ。
HashedCanonicalRequest经过哈希处理的规范请求。
4.计算签名
将SK(Access Secret Key)和创建的待签字符串作为加密哈希函数的输入,计算签名,将二进制值转换为十六进制表示形式。
signature = HexEncode(HMAC(StringToSign, Access Secret Key))
此处SK是APPKey。
5.添加签名信息到请求头
在计算签名后,将它添加到Authorization的HTTP消息头。Authorization消息头未包含在已签名消息头中,主要用于身份验证。
需要注意的是算法与access之前有空格但没有逗号,但是access与signature之前需要使用逗号隔开。
Authorization: algorithm access=Access key, signature=signature
algorithm: HMAC-SHA256
access:对于appid进行base64编码
signature:计算签名数据
示例代码如下:
import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class EncodeSignature { /** * 定义加密方式 * MAC算法可选以下多种算法 * <pre> * HmacMD5 * HmacSHA1 * HmacSHA256 * HmacSHA384 * HmacSHA512 * </pre>**/ private static final String MAC_NAME = "HmacSHA256"; private static final String ENCODING = "UTF-8"; public static String bytesToHexString(byte[] srcArray) { if ((srcArray == null) || (srcArray.length == 0)) { return ""; } StringBuffer sb = new StringBuffer(srcArray.length); for (int i = 0; i < srcArray.length; i++) { String temp = Integer.toHexString(0xFF & srcArray[i]); if (temp.length() < 2) { sb.append(0); } sb.append(temp); } return sb.toString(); } public static String encodeSHA256(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException { if ("".equals(str)) { return str; } MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(str.getBytes("UTF-8")); byte[] hashByte = md.digest(); return bytesToHexString(hashByte); } /** * 使用 HMAC-SHA256 签名方法对对encryptText进行签名 * @param encryptText 被签名的字符串 * @param encryptKey 密钥 * @return * @throws Exception */ public static byte[] HmacSHA256Encrypt(String encryptText, String encryptKey) throws Exception { byte[] data=encryptKey.getBytes(ENCODING); //根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称 SecretKey secretKey = new SecretKeySpec(data, MAC_NAME); //生成一个指定 Mac 算法 的 Mac 对象 Mac mac = Mac.getInstance(MAC_NAME); //用给定密钥初始化 Mac 对象 mac.init(secretKey); byte[] text = encryptText.getBytes(ENCODING); //完成 Mac 操作 return mac.doFinal(text); } public static void main(String[] args) throws Exception { //1、对RequestBody进行SHA-256计算,并进行Base16转码 String RequestPayload = "{\"userAccount\":\"yuthird\",\"clientType\":5,\"userName\":\"yuthird\",\"userEmail\":\"yuthird@huawei.com\",\"userPhone\":\"13511112222\"}"; System.out.println(encodeSHA256(RequestPayload)); //2、拼接待请求数据,并将待请求数据进行SHA-256计算,并进行Base16转码 String CanonicalRequest = "POST\n/rest/usg/sso/v1/auth/appauth/\ncontent-type:application/json\ndate:20190329T074551Z\n\n" + encodeSHA256(RequestPayload); String HashedCanonicalRequest = encodeSHA256(CanonicalRequest); System.out.println(HashedCanonicalRequest); //3、拼接待签名字符串,并将待签名字符串进行HMAC-SHA256计算,并Base16转码 String StringToSign = "HMAC-SHA256\n20190329T074551Z\n" + HashedCanonicalRequest; String signature = bytesToHexString(HmacSHA256Encrypt(StringToSign,"gHKag2yRtR2bP83x")); System.out.println("signature:" + signature); } }