# 签名算法说明

如前所述,云存储服务通过使用签名来验证及识别访问者的身份。

如果服务器接收到的请求中的URL是带有签名的,则服务器可以判断该请求是否是具备合法访问权限的用户身份;如果签名验证通过,则请求合法,执行请求所要求的相关操作;反之,则服务器判断请求不合法,拒绝执行相关请求操作,返回错误提示信息。

如果请求访问的是设为公开读的资源,则如果请求访问相关资源,则可不带任何签名,直接访问资源对应的URL地址、或者直接对资源进行操作即可。

签名可通过以下几种方式生成:

  • 通过SDK中的相关方法生成签名
  • 通过管理控制台的“URL签名”工具,填写相关参数,自动生成相关请求的URL签名
  • 根据下文中签名算法的具体说明来生成相关签名

我们的签名认证分为三种:

  1. HTTP Request Header(Authorization)方式
  2. URL签名认证方式
  3. Cookie认证方式

后面会分别详细介绍介绍这三种方式。

# 签名的基本形式

Signature = Base64( HMAC-SHA1( '您的SecretAccessKey', UTF-8-Encoding-Of( StringToSign ) ) );

ssig = Signature[5:15]; #截取10位字符, 从第5个开始, 第15个结束

StringToSign = HTTP-Verb + "\n" +
               Content-MD5 + "\n" +
               Content-Type + "\n" +
               Date + "\n" +
               CanonicalizedAmzHeaders +
               CanonicalizedResource;

签名值的计算方法是对一个UTF-8的字符串,用你的SecretKey(可以到控制台(https://scs.sinacloud.com (opens new window))获得)进行HMAC-SHA1加密。 然后把加密结果进行Base64编码,并截取10位字符, 从第5个开始, 第15个结束。PHP代码示例:

$ssig = substr( base64_encode( hash_hmac( "sha1", $stringToSign, $mySecretKey, true ) ), 5, 10 ) ; 

StringToSign字符串也需要满足一定的格式。如上所示,HTTP-VerbContent-MD5Content-TypeDateCanonicalizedAmzHeadersCanonicalizedResource。 他们都用换行符"\n"进行连接,下面分别对这些值进行介绍:

  • HTTP-Verb:

    表示操作名,即HTTP的请求方式,应该为PUT、GET、DELETE、HEAD和POST中的一种。

  • Content-MD5:

    StringToSign中Content-MD5的位置可以是HTTP Request Headers(请求头)中的 s-sina-sha1 或者 s-sina-md5 或者 Content-MD5的值。

    TIP

    我们推荐使用:Content-MD5,兼容AmazonS3; 优先级:如果请求头中同时出现,StringToSign中优先使用s-sina-sha1的值,如果没有出现s-sina-sha1则使用s-sina-md5s-sina-md5没有出现则使用Content-MD5,如果都未出现则留空。 需要注意的是s-sina-md5s-sina-sha1是使用hex编码的值,Content-MD5是按rfc标准使用base64编码的值。

  • Content-Type:

    请求头中的Content-Type值,如果请求头中没有出现则留空(例如:GET、HEAD请求方式的请求头中是没有Content-Type的)

  • Date:

    请求头中的Date值, 或者Query String(URL中的参数)中Expires参数的值(时间戳),代表本次请求的有效时间。如果请求头Date和Query String中Expires同时出现, 则StringToSign中优先使用Expires的值。

  • CanonicalizedAmzHeaders:

    请求头中所有以x-amz-x-sina-开头的header,将他们的key转化为小写并按顺序排列,key和value之间用冒号相连,以换行符“\n”连接和结尾,组成CanonicalizedAmzHeaders加入StringToSign参与签名。例如请求头中包含:

    X-Sina-Meta-FileIcon: page_white_code.png
    X-Amz-Meta-ReviewedBy: test@test.net
    X-Amz-Meta-FileChecksum: 0x02661779
    X-Amz-Meta-CheckSumAlgorithm: crc32
    

    那么CanonicalizedAmzHeaders为:

    x-amz-meta-checksumalgorithm:crc32\n
    x-amz-meta-filechecksum:0x02661779\n
    x-amz-meta-reviewedby:test@test.net\n
    x-sina-meta-fileicon:page_white_code.png\n
    
  • CanonicalizedResource

    CanonicalizedResource = [ "/" + Bucket ] +
    	<HTTP-Request-URI, from the protocol name up to the query string> +
    	[ sub-resource, if present. For example ("?acl", "?location", "?torrent", "?website", "?logging", "?relax", "?meta", "?uploads", "?multipart", "?part", "?copy"), or ("?uploadId=...", "ip=...", "partNumber=...")];
    

    CanonicalizedResource是指想要访问的资源:

    • 如果访问资源没有指定bucket,那么CanonicalizedResource = "/"
    • 如果包括bucket,而不包括object,那就是"/bucket_name/",注意前后都带"/"
    • 如果既包括bucket,也包括object,那么就是"/bucket_name/object_name"
    • 另外,如果您要访问的URL中带有如下的参数, 则需要加入sub-resource参与签名。

    sub-resource两种形式:

    • 不带"="querystring参数: "?acl""?location""?torrent""?website""?logging""?relax""?meta""?uploads""?multipart""?part""?copy"。每个url中最多只能出现 1个 不带"="的参数。
    • "="的key-value形式的参数: "?uploadId=...""ip=...""partNumber=..."。如果querystring里出现前面这些key-value形式的参数,需将所有的key-value参数按key的字符串升序排序后,追加到后面。

    CanonicalizedResource中带有sub-resource举例说明:

    • 含有不带"="querystring参数(?acl):/bucket_name/my_file?acl
    • 含有ip参数:/bucket_name/my_file?acl&ip=123.1.2.3
    • 含有ip和uploadID参数:/bucket_name/my_file?acl&ip=123.1.2.3&uploadID=abc123
    • 含有ip和uploadID,没有acl:/bucket_name/my_file?ip=123.1.2.3&uploadID=abc123

# 签名认证

如前所述, 我们通过签名算法构造了一个签名(ssig),下面介绍如何使用签名进行资源的访问和操作。

我们的签名认证分为三种:

  • HTTP Request Header(Authorization)方式
  • URL签名认证方式
  • Cookie认证方式

下面依次介绍:

# HTTP Request Header(Authorization)方式

我们需要在请求头中加入Authorization,格式为:

Authorization: SINA MyAccessKey:ssig 

例如上传一个文件(AccessKey=1001HBK3UF,ssig=5KABik\k+K):

PUT /file/to/my/file.txt?formatter=json HTTP/1.1
Host: my_bucket.sinacloud.net
x-amz-acl: private
Authorization: SINA 1001HBK3UF:5KABik\k+K
Date: Sat, 20 Nov 2286 17:46:39 GMT
Content-Type: text/plain
Content-Length: 12918291 

下载一个文件(AccessKey=1001HBK3UF,ssig=5KABikASeK):

GET /file/to/my/file.txt?Expires=1396513956&ip=1.2.3.4&formatter=json HTTP/1.1
Host: my_bucket.sinacloud.net
Authorization: SINA 1001HBK3UF:5KABikASeK 

# URL签名认证方式

我们需要增加QueryString(URL参数):KIDssig,格式为:

KID=sina,MyAccessKey&ssig=ssig_value

请注意

注意:如果使用URL签名认证方式,ssig需要进行urlencode编码,因为base64后会出现 + 字符。

例如上传一个文件(AccessKey=1001HBK3UF,ssig=J4wyCwmznM):

PUT /file/to/my/file.txt?KID=sina,1001HBK3UF&ssig=J4wyCwmznM&Expires=1396515387&ip=1.2.3.4&formatter=json HTTP/1.1
Host: my_bucket.sinacloud.net
x-amz-acl: private
Content-Type: text/plain
Content-Length: 12918291

下载一个文件(AccessKey=1001HBK3UF,ssig=5KABikASeK):

GET /file/to/my/file.txt?KID=sina,1001HBK3UF&ssig=5KABikASeK&Expires=1396515387&ip=1.2.3.4&formatter=json HTTP/1.1
Host: my_bucket.sinacloud.net 

# Cookie认证方式

当需要使用Cookie指定验证签名时,我们需要增加QueryString(URL参数):cheese, 它的值为Cookie的名字。例如:

cheese=cookie_name 

Cookie格式为(Cookie中只支持ssigExpires):

Cookie: cookie_name=urlencode("ssig=ssig_value&Expires=Expires_value") 

请注意

注意:cookie_name的值和ssig的值,都需要urlencode

例如下载一个文件(AccessKey=1001HBK3UF,ssig=5KABikASeK):

GET /file/to/my/file.txt?KID=sina,1001HBK3UF&ip=1.2.3.4&formatter=json&cheese=hehe123 HTTP/1.1
Host: my_bucket.sinacloud.net
Cookie: hehe123=urlencode(ssig=5KABikASeK&Expires=1396515387) 

# 典型示例

# 列出所有的bucket

请求:

GET /?formatter=json HTTP/1.1
Host: sinacloud.net
Date: Sat, 20 Nov 2286 17:46:39 GMT
Authorization: SINA 1001HBKAUX:J4wyCwmznM 

签名:

$stringToSign = "GET" + "\n" + 
		"\n" + 
		"\n" + 
		"Sat, 20 Nov 2286 17:46:39 GMT" + "\n" + 
		"/";
		
$ssig = substr( base64_encode( hash_hmac( "sha1", $stringToSign, $mySecretKey, true ) ), 5, 10 ) ;

请求:

GET /?formatter=json&KID=sina,1001HBKAUX&ssig=J4wyCwmznM&Expires=1396532775 HTTP/1.1
Host: sinacloud.net 

签名:

$stringToSign = "GET" + "\n" + 
		"\n" + 
		"\n" + 
		"1396532775" + "\n" + 
		"/";
		
$ssig = substr( base64_encode( hash_hmac( "sha1", $stringToSign, $mySecretKey, true ) ), 5, 10 ) ; 

# 列文件

请求:

GET /?formatter=json HTTP/1.1
Host: bucket-name.sinacloud.net
Date: Thu, 03 Apr 2014 13:46:16 GMT
Authorization: SINA 1001HBKAUX:lFcLwnk4C/ 

签名:

$stringToSign = "GET" + "\n" + 
		"\n" + 
		"\n" + 
		"Thu, 03 Apr 2014 13:46:16 GMT" + "\n" + 
		"/bucket_name/";
		
$ssig = substr( base64_encode( hash_hmac( "sha1", $stringToSign, $mySecretKey, true ) ), 5, 10 ) ; 

# 上传文件

请求:

PUT /path/to/my/file.txt?formatter=json HTTP/1.1
x-amz-acl: private
x-amz-meta-UploadLocation: My Home
Host: bucket_name.sinacloud.net
Date: Thu, 03 Apr 2014 14:00:28 GMT
Content-MD5: htUc53U6NgeQQfwV9ySANQ==
Content-Type: text/plain
Authorization: SINA 1001HBKAUX:Wnc9TJmvVZ

BODY正文部分省略

签名:

$stringToSign = "PUT" + "\n" + 
		"htUc53U6NgeQQfwV9ySANQ==" + "\n" + 
		"text/plain" + "\n" + 
		"Thu, 03 Apr 2014 14:00:28 GMT" + "\n" + 
		"x-amz-acl:private" + "\n" + "x-amz-meta-uploadlocation:My Home" + "\n" + 
		"/bucket_name/path/to/my/file.txt";

$ssig = substr( base64_encode( hash_hmac( "sha1", $stringToSign, $mySecretKey, true ) ), 5, 10 ) ;

请求:

PUT /path/to/my/file.txt?formatter=json&KID=sina,1001HBKAUX&ssig=J4wyCwmznM&Expires=1396532775 HTTP/1.1
x-amz-acl: private
x-amz-meta-UploadLocation: My Home
Host: bucket_name.sinacloud.net
Content-MD5: htUc53U6NgeQQfwV9ySANQ==
Content-Type: text/plain

BODY正文部分省略 

签名:

$stringToSign = "PUT" + "\n" + 
		"htUc53U6NgeQQfwV9ySANQ==" + "\n" + 
		"text/plain" + "\n" + 
		"1396532775" + "\n" + 
		"x-amz-acl:private" + "\n" + "x-amz-meta-uploadlocation:My Home" + "\n" + 
		"/bucket_name/path/to/my/file.txt";
		
$ssig = substr( base64_encode( hash_hmac( "sha1", $stringToSign, $mySecretKey, true ) ), 5, 10 ) ; 

# 获取文件信息

请求:

HEAD /path/to/my/file.txt?formatter=json HTTP/1.1
Host: bucket_name.sinacloud.net
Date: Thu, 03 Apr 2014 14:27:41 GMT
Authorization: SINA 1001HBKAUX:ojecD2wl9d

签名:

$stringToSign = "HEAD" + "\n" + 
		"\n" + 
		"\n" + 
		"Thu, 03 Apr 2014 14:27:41 GMT" + "\n" + 
		"/bucket_name/path/to/my/file.txt";

$ssig = substr( base64_encode( hash_hmac( "sha1", $stringToSign, $mySecretKey, true ) ), 5, 10 ) ; 

# 设置ACL

请求:

PUT /bucket_name/file?acl&formatter=json HTTP/1.1
Host: sinacloud.net
Date: Thu, 03 Apr 2014 14:35:15 GMT
Content-Type: application/json
Authorization: SINA 1001HBKAUX:Or5quZmjRK

BODY

签名:

$stringToSign = "PUT" + "\n" + 
		"\n" + 
		"application/json" + "\n" + 
		"Thu, 03 Apr 2014 14:35:15 GMT" + "\n" + 
		"/bucket_name/file?acl";

$ssig = substr( base64_encode( hash_hmac( "sha1", $stringToSign, $mySecretKey, true ) ), 5, 10 ) ; 

# 下载文件

请求:

GET /path/to/my/file.txt?KID=sina,1001hbk3aV&Expires=1396569436&ip=1.2.3.4&ssig=WJdSQWSZEF&fn=custom_file_name.txt HTTP/1.1
Host: bucket-name.sinacloud.net 

签名:

$stringToSign = "GET" + "\n" + 
		"\n" + 
		"\n" + 
		"1396569436" + "\n" + 
		"/bucket_name/path/to/my/file.txt?ip=1.2.3.4";

$ssig = substr( base64_encode( hash_hmac( "sha1", $stringToSign, $mySecretKey, true ) ), 5, 10 ) ; 

# 功能参数说明

上面这些例子中的URL出现了如:ipExpires等参数,下面介绍一下它们的功能:

# QueryString 功能定义
1 KID 用户的身份标示,验证后将使用该帐号的权限访问文件,格式: KID=sina,accesskey, 可以在控制台获得accesskey, 然后前面带上"sina,"
2 Expires 请求的截止时间(时间戳),限制本次请求必须在给定时间戳前完成。参照签名算法中的StringToSign:Date说明
3 ssig 签名验证串
4 ip ip地址验证限制,可以直接写授权的ip或启用时间+授权的ip,在超过启动时间时只允许该ip地址的用户下载。例如:ip=1396569436,1.2.3. 表示ip地址被限制在1.2.3.网段,并在UNIX时间1396569436后生效,ip=1.2.3.4 表示只有ip为1.2.3.4的用户可以访问
5 cheese 进行cookie验证,服务器端会读取由cheese所指定的cookie的值,参照签名验证中的Cookie认证方式
6 fn 下载文件名,当存在该选项时服务器将强行返回Content-Disposition, 如果fn的值不为空,则以该值为文件名,否则会自动截取url的最后一段为文件名