OpenAPI 签名
简介
用户在访问网易云的 OpenAPI 时,都需要对用户发送到网易云的 HTTP 请求进行签名,以便于网易云能够识别发送请求的用户。用户需要使用访问密钥来对请求进行签名,访问密钥由网易云颁发的 Access Key 和 Access Secret 组成,具体的签名步骤参见文档的 OpenAPI 签名流程部分。
访问密钥
注册后,网易云会颁发 Access Key 和 Access Secret 给用户。没有访问密钥将无法调用任何接口。
登录控制台后用户可以在Access Key位置查看自己的 Access Key 和 Access Secret。
说明:
- Access Key: 用于标识用户身份,Access Key 会作为一个请求参数,在网络请求中会以某种形式传输
- Access Secret: 作为私钥形式存储于客户方本地,不在网络传递,它的作用是对客户方发起的请求进行数字签名,保证该请求是来自指定客户的请求,并且是合法的有效的
使用 Access Key 进行身份识别,加上 Access Secret 进行数字签名,即可完成应用接入与认证授权。 你可以在 Access Key 中创建、下载、删除、禁用你的 Access Key。 网易云支持多 Access Key、Access Secret,保障你的应用安全。
OpenAPI 签名流程
为什么要签名
通过对请求的签名,可以达到以下目的:
- 识别访问网易云 OpenAPI 的用户的身份
- 防止在传输过程中请求被篡改。由于发送到网易云的请求都需要使用 Access Secret 计算签名,网易云收到请求后会使用同样的 Access Secret 对请求计算签名,当签名不一致时则会拒绝该请求。
- 防止潜在的重放攻击。用户的请求必须在请求中的时间戳的15分钟内发送到网易云,否则网易云将会拒绝该请求。
签名版本
提供了两个签名版本,1.0 于 2.0,网易云基础服务的所有服务(NOS 除外)都同时支持两个版本的签名,您可以任意选择。
请求结构
通信协议
为了提高请求的安全性,网易云 API 的所有接口均通过 HTTPS 进行通信。
服务地址
网易云 API 的服务接入地址如下所示:
地域 | 服务地址 |
---|---|
华东1 | open.cn-east-1.163yun.com 或 open.c.163.com |
华东3 | open.cn-east-3.163yun.com |
请求方法
支持 GET 或 POST 请求,每个接口具体支持的 HTTP 方法会在具体的接口文档中说明。需要注意的是,如果接口使用的是 POST 方法,则业务参数放在 Request Body 中,而公共参数需要放到 QueryString 部分。
字符编码
请求及相应都使用 UTF-8
进行编码
请求响应
Attention
- 通常情况下接口的响应都是 JSON 格式。
- 建议用户在反馈问题时带上 Request-Id 。
网易云 API 的响应都会增加一个名为 Request-Id 的 header,其值为请求的 UUID。通过在客户端、服务器或任何支持服务上记录该值,它能为我们提供一种机制来跟踪、诊断和调试请求。
用户可以根据 API 响应的 http status code 来判断请求是否成功。
- 响应 http status code 2xx: 请求成功。
- 响应 http status code 4xx: 请求失败,客户端错误。通常是由于:无权限、参数错误等原因造成,一般情况下需要根据响应提示修改您的请求。
- 响应 http status code 5xx: 请求失败,服务端错误。服务端发生了某些未知错误导致了请求失败。
通常情况下,请求失败时在响应中会有 Code、 Message、 RequestId 三个参数,其中 Code、 Message 用于提示错误原因,而 RequestId 与 header 中的 Request-Id 一致,都是请求的 UUID。
成功时的响应示例:
http status code 200
{
"RequestId":"bcdf0f8b-767d-4a9e-b63f-e188ce5af96f",
"InstanceId":1234,
"InstanceName":"MyWorkload"
}
失败时的响应示例:
http status code 400
{
"RequestId":"bcdf0f8b-767d-4a9e-b63f-e188ce5af96f",
"Code":"MissingParameter",
"Message":"The required input parameter InstanceId for processing this request is not supplied."
}
Note
签名示例代码下载
使用示例:
./signature.py --region=cn-east-1 \
--service=nvm \
--access-key=$key \
--access-secret=$secret \
-X POST \
-H 'Content-Type: application/json' \
--data "$json_body" \
"$host_port/ssl?Action=$action&Version=$version"
1.0 版本签名
公共请求参数
参数名称 | 是否必填 | 说明 |
---|---|---|
Action | 是 | 接口的指令名称,每个接口都对应一个 Action,见具体的接口文档。如:查询云服务器列表接口,其 Action 为 DescribeServers |
Version | 是 | API 的版本号,其格式为 yyyy-MM-dd,如 2017-11-16 |
Region | 是 | 目标资源所在的地域,当您请求华东1的资源时,其值为 cn-east-1,当您请求华东3的资源时,其值为 cn-east-3,。 需要注意的是,Region 参数必须与您请求中的服务地址相对应,比如:请求华东1的资源时,服务地址为 open.cn-east-1.163yun.com ,Region 参数的值为 cn-east-1 |
AccessKey | 是 | 访问密钥中的 Access Key ,用于唯一标识一个用户 |
Timestamp | 是 | 客户端请求的时间戳,日期格式按照 ISO8601 标准格式,并需要使用 UTC 时间。格式为: YYYY-MM-DDThh:mm:ssZ。如:2018-01-08T06:20:00 (北京时间:2018年1月8日14点20分00秒。需要注意的是,您的请求需要在该时间戳的15分钟以内发送到网易云,否则网易云将拒绝该请求。 |
SignatureVersion | 是 | 签名版本,当前版本的值为 1.0 |
SignatureMethod | 是 | 计算签名时 Hmac 的算法,当前只支持 HMAC-SHA256 |
SignatureNonce | 是 | 唯一随机数,用于防止网络重放攻击,每个请求都需要使用不同的值 |
Signature | 是 | 请求计算出来的签名 |
DryRun | 否 | true/false。使用该参数时,请求只用于校验签名信息是否正确,而不进行实际的处理。当签名正确并且该参数的值为 true 时,返回 DryRunOperation 错误 |
签名流程
客户端在请求时,需要按照如下步骤生成签名 Signature:
- 1 在原始请求的基础上添加公共请求参数(Signature 参数除外,Signature 参数需要在签名计算完成后添加)。
- 2 构造标准查询字符串 CanonicalizedQueryString
- 2.1 将所有 QueryString 部分参数的 key/value 都分别使用 UTF-8 字符集进行 URL 编码。
- 2.2 将 2.1 处理后的参数按照字典序进行排序。
- 2.3 将 2.2 处理后的参数 key/value 使用 = 连接,不同请求参数之间使用 & 连接。
- 3 计算 HashedPayload。使用
SHA256
哈希算法对 HTTPS 请求正文中的负载(RequestBody)创建哈希值,若负载为空,则使用空字符串参数计算。伪代码如下:HashedPayload = Lowercase(HexEncode(Hash(RequestPayload)))
- 4 计算 String2Sign。伪代码如下:(其中
\n
为换行符)String2Sign = HTTPMethod + '\n' + host + '\n' + '/servicename' + '\n' + CanonicalizedQueryString + '\n' + HashedPayload
- 5 计算签名。使用 HMAC-SHA256 协议创建基于哈希的消息身份验证代码 (HMAC),然后计算签名,所得签名进行 Base-64 编码。
- 6 请求中添加签名。将 Signature 参数添加到请求参数中,并且和其他请求参数一样也需要进行 URLEncode 。
计算 String2Sign 过程中进行 URL 编码与一般采用application/x-www-form-urlencode
MIME 格式编码算法有如下的差异:
字符 | application/x-www-form-urlencode MIME 格式编码结果 | UTF-8 字符集进行 URL 编码结果 |
---|---|---|
空格
| + | %20 |
* | 不编码 | %2A |
~ | %7E | 不编码 |
Attention
- 1.0 版本签名,所有的公共请求参数都需要放在 QueryString 部分。
- 在第 2 步中的对参数使用 UTF-8 字符集进行 URL 编码时,需要注意的是这里使用的编码方式和一般采用
application/x-www-form-urlencode
MIME 格式编码算法有所差异,具体的编码算法参见 RFC 3986 - 在计算签名时,host 需要使用实际请求蜂巢 OpenAPI 时的 host。以避免代理转发情况下,参与签名计算的 host 与实际请求的 host 不一致,从而导致签名不一致。
签名示例
以容器服务(nvm)的:有状态容器查询全部空间下的负载列表接口为例:
接口特定参数:
参数名 | 描述 | 示例 |
---|---|---|
Action | DescribeStatefulWorkloadsAllNamespaces | |
Version | 2017-11-16 |
则查询华东某个租户全部空间下的负载列表,原始请求为:
https://open.cn-east-1.163yun.com/nvm?Action=DescribeStatefulWorkloadsAllNamespaces&Version=2017-11-16
1 对该请求增加公共请求参数,则该请求的所有请求参数为:
Action=DescribeStatefulWorkloadsAllNamespaces
Version=2017-11-16
AccessKey=f9785e03d192401ab2464b8ca63c6e8f
Timestamp=2018-01-29T04:43:02Z
SignatureVersion=1.0
SignatureMethod=HMAC-SHA256
SignatureNonce=e616388b-2509-4d29-834d-473d0f7756d2
Region=cn-east-1
2 构造标准查询字符串 CanonicalizedQueryString:
构造所有的请求参数,并对其进行编码,编码后为:
Action=DescribeStatefulWorkloadsAllNamespaces
Version=2017-11-16
AccessKey=f9785e03d192401ab2464b8ca63c6e8f
Timestamp=2018-01-29T04%3A43%3A02Z
SignatureVersion=1.0
SignatureMethod=HMAC-SHA256
SignatureNonce=e616388b-2509-4d29-834d-473d0f7756d2
Region=cn-east-1
将所有请求参数按照字典序进行排序结果为:
AccessKey=f9785e03d192401ab2464b8ca63c6e8f
Action=DescribeStatefulWorkloadsAllNamespaces
Region=cn-east-1
SignatureMethod=HMAC-SHA256
SignatureNonce=e616388b-2509-4d29-834d-473d0f7756d2
SignatureVersion=1.0
Timestamp=2018-01-29T04%3A43%3A02Z
Version=2017-11-16
将所有请求参数 Key / Value 使用 = 连接,不同的参数之间使用 & 连接,则最后生成的规范查询字符串 (CanonicalizedQueryString) 为:
AccessKey=f9785e03d192401ab2464b8ca63c6e8f&Action=DescribeStatefulWorkloadsAllNamespaces&Region=cn-east-1 &SignatureMethod=HMAC-SHA256&SignatureNonce=e616388b-2509-4d29-834d-473d0f7756d2 &SignatureVersion=1.0&Timestamp=2018-01-29T04%3A43%3A02Z&Version=2017-11-16
3 计算请求负载的哈希值,由于该请求为 GET 方法,无 RequestBody,故使用空字符串参与计算。计算结果为:
HashedPayload=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
4 构造 String2Sign,则 String2Sign 结果为:
GET\n
open.cn-east-1.163yun.com\n
/nvm\n
AccessKey=f9785e03d192401ab2464b8ca63c6e8f&Action=DescribeStatefulWorkloadsAllNamespaces&Region=cn-east-1&SignatureMethod=HMAC-SHA256&SignatureNonce=e616388b-2509-4d29-834d-473d0f7756d2&SignatureVersion=1.0&Timestamp=2018-01-29T04%3A43%3A02Z&Version=2017-11-16\n
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
5 假设当前 AccessKey 对应的 SecretAccessKey 为 8cfe7d5bc07949c8af7c399e19e6a346,则使用该 SK 对上述过程生成的 String2Sign 使用 HMAC-SHA256 协议计算签名并进行 Base-64 编码后,得到的签名结果为:
Yk82PRf5A8uDQ7623iwOwAll3MCHSwQpGVdq2PobYzs=
6 将计算得到的签名作为参数拼接到 url 中(需要遵循同样的规则进行 URLEncode ),则最后的请求为:
GET https://open.cn-east-1.163yun.com/nvm?AccessKey=f9785e03d192401ab2464b8ca63c6e8f&Action=DescribeStatefulWorkloadsAllNamespaces&Region=cn-east-1&SignatureMethod=HMAC-SHA256&SignatureNonce=e616388b-2509-4d29-834d-473d0f7756d2&SignatureVersion=1.0&Timestamp=2018-01-29T04:43:02Z&Version=2017-11-16&Signature=Yk82PRf5A8uDQ7623iwOwAll3MCHSwQpGVdq2PobYzs%3D
2.0 版本签名
公共请求参数
Action
接口的指令名称,每个接口都对应一个 Action,见具体的接口文档。如:查询云服务器列表接口,其 Action 为 DescribeServers。
Version
API 的版本号,其格式为 yyyy-MM-dd,如 2017-11-16。
X-163-Credential
请求的凭证,是由:AK、请求日期、请求资源所在 region 、请求的服务名称、特定字符串(163_request)组成的字符串。格式为: AccessKey/YYYYMMDD/region/serviceName/163_request。
Note
- 请求日期为 ISO8601 标准, UTC 时间对应的日期,不包含时间,格式为:YYYYMMDD 。
- 资源所在 region,当前可用值为:cn-east-1、cn-north-1 且 cn-east-1 对应域名 open.cn-east-1.163yun.com。 cn-north-1 对应域名 open.cn-north-1.163yun.com。 若请求时使用的域名与该参数不能对应,则认为请求错误。
X-163-Date
客户端请求的时间戳,每个请求需使用不同的时间戳。格式按照 ISO8601 标准表示,并需要使用 UTC 时间,格式为:YYYY-MM-DDThh:mm:ssZ。
如:2017-09-06T11:00:00Z(北京时间 2017 年 9 月 6 号 19 点 0 分 0 秒)。
客户端请求的时间戳与服务端收到请求的时间戳相差绝对值为 15 分钟以内则认为 X-163-Date 参数合法,否则则认为 X-163-Date 参数非法。
Note
- X-163-Date 可以提前或落后服务器时间 15 分钟以内,超过 15 分钟则服务器会拒绝掉请求。
- 若请求中已经包含名为 date 的 header,则可以不使用该参数,默认解析顺序为:先解析 X-163-Date ,若没有,则在继续解析名为 date 的 header
X-163-SignedHeaders
参与签名计算的 header 的名称列表,不同的 header 名称使用分号分隔。Host header 是必须有的,X-163-Date 与 Date 可以二选一,其余标准 header 是可选的。
如:host;content-type;x-163-date。
X-163-SignatureVersion
签名算法版本 2.0。
X-163-SignatureMethod
当前只支持 HMAC-SHA256。
X-163-SignatureNonce
唯一随机数,用于防止网络重放攻击,每个请求使用不同的值,最大长度为64位。
X-163-DryRun
增加该参数可以用于校验签名信息是否正确,不进行实际的处理。若签名校验失败,则返回签名计算中每一步的详细信息,客户端可以根据提示的信息判断出哪一步骤发生了错误从而进行进一步修改。Boolean 类型。
X-163-Signature
计算得到的签名信息
签名流程
客户端在请求时,需要按照如下步骤生成签名 Signature。
- 1 构建规范请求。使用伪代码则表示如下:
CanonicalRequest = HTTPRequestMethod + '\n' + CanonicalURI + '\n' + CanonicalQueryString + '\n' + CanonicalHeaders + '\n' + SignedHeaders + '\n' + Lowercase(HexEncode(Hash(RequestPayload)))
- 1.1、添加请求方法,后跟换行符
- 1.2、添加规范 URI,后跟换行符。如: /nvm
- 1.3、添加规范查询字符串,后跟换行符。若最后将公共参数以及签名信息放在 queryString 中,则该步骤中的查询参数还需要包含公共参数。
- 将所有查询参数的 key / value 进行 URL 编码
- 将所有查询参数按照 key 进行字典序排序
- 将所有查询参数的 key / value 使用 = 连接
- 将所有查询参数使用 & 连接
- 1.4、添加规范 header ,后跟换行符。规范 header 是指所有参与签名的 header,其中 host header 是必须的, x-163-date / date header 其中一个是必填的,其余标准 header 是可选的。若将公共参数以及签名信息放在 header 中,则公共参数的 header 也需要添加到规范 header 中(X-163-SignedHeaders、X-163-Signature、Authorization 除外,因为这三个公共参数的值是动态构造生成的)
- 将所有签名 header 的名称转换为小写
- 将所有签名 header 按照 header 名称进行字典序排序
- 将所有签名 header 的 value 的前导、后置空格删除(trim),将多个连续的空格转换为一个
- 将 header 的 name / value 使用 : 连接
- 将上述处理完成的所有 header 后都添加 \n (换行符)
- 1.5、添加已签名 header (SignedHeaders) , 该 header 的值是规范 header 中所有 header 的名称列表(名称应该都转换为小写),通过该 header 即可标明哪些 header 参与了签名计算。其值为:将所有的规范 header 的名称使用';'连接,后跟换行符
- 1.6、使用 SHA256 哈希函数以基于 HTTP 或 HTTPS 请求正文中的负载(body)创建哈希值,如果负载(body)为空,则使用空字符串参与计算。计算使用的哈希函数是 sha-256。
HashedPayload = Lowercase(HexEncode(Hash(requestPayload)))
- 1.7、将上述所有步骤生成的字符串拼接为一个字符串。值得注意的是:规范 header 与已签名 header 之间有一个空行
- 1.8、使用 SHA256 哈希函数对上述步骤中生成的规范请求字符串创建哈希,得到 HashedCannoicalRequest
- 2 生成代签名字符串 String2Sign 。使用伪代码则表示如下:
StringToSign = Algorithm + \n + RequestDateTime + \n + CredentialScope + \n + HashedCanonicalRequest
- 2.1、添加签名过程中使用到的哈希算法,后跟换行符。当前默认 HMAC-SHA256 。(请求计算签名过程中需要使用到哈希算法的地方,都需要与这里指定的哈希算法一致。)
- 2.2、添加请求时间,后跟换行符。日期使用 ISO8601 格式,UTC 时间,如:北京日期 2017年9月14日12点00分00秒 表示为 2017-09-14T04:00:00Z. (请求计算签名过程中需要使用到时间戳的地方,其值都需要与这里指定的请求时间保持一致,使用到日期的地方,其值都需要与这里指定的请求时间中日期部分保持一致。)
- 2.3、添加请求凭证范围,后跟换行符。该凭证范围的值是:日期/region/服务/163_request 。其中日期是请求时间的日期,不包含时间值。
- 2.4、添加 1 步中生成的规范请求的哈希
- 3、派生出签名秘钥 Signing Key。该签名秘钥是有用户访问秘钥派生出来的。使用伪代码则表示如下:
HMAC(HMAC(HMAC(HMAC("163" + SK ,"YYYYMMDD"), region), 服务), "163_request")
- 3.1、使用"163" 加上 SK 作为 key1,并用其对 日期 使用 HMAC-SHA256 算法计算出哈希值,得到 key2
- 3.2、将 key2 与 region 使用 HMAC-SHA256 算法计算出哈希值,得到 key3
- 3.3、将 key3 与 服务名称 使用 HMAC-SHA256 算法计算出哈希值,得到 key4
- 3.4、将 key4 与 服务名称 使用 HMAC-SHA256 算法计算出哈希值,得到 签名秘钥 Signing Key
- 4、计算签名。将第 3 步派生出的签名秘钥 Signing Key 与第 2 步生成的 String2Sign 作为加密哈希函数的输入,将得到的哈希值转换为十六进制表示形式。
- 5、将签名信息添加到请求中。共有两种方式:(1)使用 Authorization header。(2)添加到 query string 中。但是需要注意的是,只能选择其中一种方式。(默认为:添加到 query string 的方式)
- 5.1、使用 Authorization header,则 Authorization 的值为:
其中 SignatureMethod 为签名过程中使用的哈希算法; credential scope 为 2.c 步生成的凭证范围; SignedHeaders 为 1.e 步生成的已签名 header 名称列表; Signature 为 4 步生成的签名。若使用 Authorization header 来标明签名信息,则公共参数中的 X-163-SignatureMethod 、X-163-Credential 、X-163-SignedHeaders 都不需要。Authorization: SignatureMethod Credential=AccessKey/credential_scope, SignedHeaders=SignedHeaders, Signature=signature
- 5.2、使用添加 queryString 的方式,则需要在查询参数中增加 X-163-SignatureMethod 、 X-163-Credential 、 X-163-SignedHeaders 、 X-163-Signature 四个参数,且前三个参数需要添加到 1.c 步中的规范查询字符串中。