簽名機(jī)制
簽名機(jī)制
ROA風(fēng)格API簽名包含兩部分:公共請(qǐng)求頭(HTTP 協(xié)議 header 參數(shù)和阿里云協(xié)議 Header 參數(shù))和 CanonicalizedResource(規(guī)范資源)。推薦盡量使用SDK接入,用戶可以不關(guān)注簽名細(xì)節(jié)。
名稱 | 描述 |
Authorization | 用于驗(yàn)證請(qǐng)求合法性的認(rèn)證信息,采用 acs AccessKeyId:signature 格式。 |
Content-Length | RFC 2616 中定義的 HTTP 請(qǐng)求內(nèi)容長(zhǎng)度。 |
Content-Type | RFC 2616 中定義的 HTTP 請(qǐng)求內(nèi)容類型。 |
Content-MD5 | HTTP 協(xié)議消息體的 128-bit MD5 散列值轉(zhuǎn)換成 BASE64 編碼的結(jié)果。為了防止所有請(qǐng)求被篡改,建議所有請(qǐng)求都附加該信息。 |
Date | 描述請(qǐng)求時(shí)間,GMT 格式,如:Wed, 26 Aug. 2015 17:01:00 GMT。 |
Accept | 客戶端需要的返回值類型,只支持 application/json |
Host | 訪問(wèn) Host 值,例如:mt.cn-hangzhou.aliyuncs.com。 |
x-acs-signature-nonce | 唯一隨機(jī)數(shù),用于防止網(wǎng)絡(luò)重放攻擊。用戶在不同請(qǐng)求中要使用不同的隨機(jī)數(shù)值。 |
x-acs-signature-method | 簽名方法,目前只支持 HMAC-SHA1。 |
x-acs-version | API版本, 如果不填寫(xiě),服務(wù)端默認(rèn)取最高版本 |
簽名計(jì)算方法
ROA風(fēng)格的API請(qǐng)求使用標(biāo)準(zhǔn)的Authorization頭來(lái)簽名自己的請(qǐng)求,請(qǐng)求格式如下:
Authorization: acs AccessKeyId:Signature
計(jì)算body的MD5值,然后再對(duì)其進(jìn)行base64編碼,編碼后的值設(shè)置到 Header中。
使用請(qǐng)求中的Header參數(shù)構(gòu)造規(guī)范化的Header字符串
headerStringToSign = HTTP-Verb + "\n" + //HTTP_Verb只支持POST Accept + "\n" + //Accept為application/json Content-MD5 + "\n" + //第1步中計(jì)算出來(lái)的MD5值 Content-Type + "\n" + //Content-Type值為application/json;chrset=utf-8 Date + "\n" + //Date值為GMT時(shí)間 “x-acs-signature-method:HMAC-SHA1\n” + “x-acs-signature-nonce:” + ${x-acs-signature-nonce} + "\n" + “x-acs-version:2019-01-02" + "\n";
CanonicalizedResource 表示客戶想要訪問(wèn)資源的規(guī)范描述,需要將子資源和qurey一同按照字典序,從小到大排列并以 & 為分隔符生成子資源字符串(?后的所有參數(shù)),示例如下(alimt所有請(qǐng)求都不帶參數(shù))。
resourceStringToSign = URI;
將上兩步構(gòu)造的規(guī)范化字符串按照下面的規(guī)則構(gòu)造成待簽名的字符串。
stringToSign = headerStringToSign + resourceStringToSign;
按照 RFC2104的定義,計(jì)算待簽名字符串StringToSign的 HMAC 值,按照 Base64 編碼規(guī)則把上面的 HMAC 值編碼成字符串,并在前面加上AccessKeyId,即得到簽名值(Authorization),示例如下:
Signature = Base64( HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) ) ) Authorization = "acs " + AccessKeyId + ":" + Signature
代碼示例(java)
import sun.misc.BASE64Encoder;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.UUID;
public class Sender {
/*
* 計(jì)算MD5+BASE64
*/
public static String MD5Base64(String s) {
if (s == null)
return null;
String encodeStr = "";
byte[] utfBytes = s.getBytes();
MessageDigest mdTemp;
try {
mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(utfBytes);
byte[] md5Bytes = mdTemp.digest();
BASE64Encoder b64Encoder = new BASE64Encoder();
encodeStr = b64Encoder.encode(md5Bytes);
} catch (Exception e) {
throw new Error("Failed to generate MD5 : " + e.getMessage());
}
return encodeStr;
}
/*
* 計(jì)算 HMAC-SHA1
*/
public static String HMACSha1(String data, String key) {
String result;
try {
SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(data.getBytes());
result = (new BASE64Encoder()).encode(rawHmac);
} catch (Exception e) {
throw new Error("Failed to generate HMAC : " + e.getMessage());
}
return result;
}
/*
* 獲取時(shí)間
*/
public static String toGMTString(Date date) {
SimpleDateFormat df = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z", Locale.UK);
df.setTimeZone(new java.util.SimpleTimeZone(0, "GMT"));
return df.format(date);
}
/*
* 發(fā)送POST請(qǐng)求
*/
public static String sendPost(String url, String body, String ak_id, String ak_secret) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
/*
* http header 參數(shù)
*/
String method = "POST";
String accept = "application/json";
String content_type = "application/json;chrset=utf-8";
String path = realUrl.getFile();
String date = toGMTString(new Date());
String host = realUrl.getHost();
// 1.對(duì)body做MD5+BASE64加密
String bodyMd5 = MD5Base64(body);
String uuid = UUID.randomUUID().toString();
String stringToSign = method + "\n" + accept + "\n" + bodyMd5 + "\n" + content_type + "\n" + date + "\n"
+ "x-acs-signature-method:HMAC-SHA1\n"
+ "x-acs-signature-nonce:" + uuid + "\n"
+ "x-acs-version:2019-01-02\n"
+ path;
// 2.計(jì)算 HMAC-SHA1
String signature = HMACSha1(stringToSign, ak_secret);
// 3.得到 authorization header
String authHeader = "acs " + ak_id + ":" + signature;
// 打開(kāi)和URL之間的連接
URLConnection conn = realUrl.openConnection();
// 設(shè)置通用的請(qǐng)求屬性
conn.setRequestProperty("Accept", accept);
conn.setRequestProperty("Content-Type", content_type);
conn.setRequestProperty("Content-MD5", bodyMd5);
conn.setRequestProperty("Date", date);
conn.setRequestProperty("Host", host);
conn.setRequestProperty("Authorization", authHeader);
conn.setRequestProperty("x-acs-signature-nonce", uuid);
conn.setRequestProperty("x-acs-signature-method", "HMAC-SHA1");
conn.setRequestProperty("x-acs-version", "2019-01-02"); // 版本可選
// 發(fā)送POST請(qǐng)求必須設(shè)置如下兩行
conn.setDoOutput(true);
conn.setDoInput(true);
// 獲取URLConnection對(duì)象對(duì)應(yīng)的輸出流
out = new PrintWriter(conn.getOutputStream());
// 發(fā)送請(qǐng)求參數(shù)
out.print(body);
// flush輸出流的緩沖
out.flush();
// 定義BufferedReader輸入流來(lái)讀取URL的響應(yīng)
InputStream is;
HttpURLConnection httpconn = (HttpURLConnection) conn;
if (httpconn.getResponseCode() == 200) {
is = httpconn.getInputStream();
} else {
is = httpconn.getErrorStream();
}
in = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("發(fā)送 POST 請(qǐng)求出現(xiàn)異常!" + e);
e.printStackTrace();
}
// 使用finally塊來(lái)關(guān)閉輸出流、輸入流
finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result;
}
}