云主机

  • OpenAPI签名验证算法

    最近更新时间:2019-01-17 18:58:14

    对于每一次HTTP或者HTTPS协议请求,我们会根据访问中的签名信息验证访问请求者身份。具体由使用PublicKey和SecretKey实现,可以在这里查看

    其中PublicKey是访问者身份,SecretKey是加密签名字符串和服务端验证签名字符串的密钥,因此请您必须严格保密谨防泄露。

    步骤 1. 构造规范化请求字符串

    1. 排序参数。排序规则以首字母顺序排序,排序参数包括 公共请求参数 和接口自定义参数,不包括公共请求参数中的 signature 参数。
      当使用GET方法提交请求时,这些参数就是请求URL中的参数部分,即URL中 ? 之后由 & 连接的部分。

    2. 编码参数。使用UTF-8字符集按照 RFC3986 规则编码请求参数和参数取值,编码规则如下:
      a. 字符A~Z、a~z、0~9以及字符 -、_、.、~ 不编码。
      b. 其它字符编码成 %XY 的格式,其中 XY 是字符对应ASCII码的16进制。示例:半角双引号(")对应 %22。
      c. 扩展的UTF-8字符,编码成 %XY%ZA… 的格式。
      d. 空格( )编码成 %20,而不是加号(+)。

      该编码方式与 application/x-www-form-urlencoded MIME格式编码算法相似,但又有所不同。

      如果您使用的是Java标准库中的 java.net.URLEncoder,可以先用标准库中 percentEncode 编码,随后将编码后的字符中加号(+)替换为 %20、星号(*)替换为 %2A、%7E 替换为波浪号(~),即可得到上述规则描述的编码字符串。

      如下:

      private static final String ENCODING = "UTF-8";
      private static String percentEncode(String value) throws UnsupportedEncodingException {
       return value != null ? URLEncoder.encode(value, ENCODING).replace("+", "%20").replace("*", "%2A").replace("%7E", "~") : null;
      }
      
    3. 使用等号(=)连接编码后的请求参数和参数取值。

    4. 使用与号(&)连接编码后的请求参数,注意参数排序与 步骤1 一致。

    5. 使用与号(&)连接编码后的Body数据,注意参数排序与 步骤1 一致。

    现在,您得到了规范化请求字符串(CanonicalizedQueryString),其结构遵循 请求结构。

    步骤 2. 构造签名字符串

    构造待签名字符串 StringToSign。您可以同样使用 percentEncode 处理上一步构造的规范化请求字符串,规则如下:

    1. 构造待签名字符串 StringToSign。您可以同样使用 percentEncode 处理上一步构造的规范化请求字符串,规则如下:

      StringToSign= HTTPMethod + "&" + //HTTPMethod:发送请求的 HTTP 方法,例如 GET。
       percentEncode("/PATH") + "&" + //percentEncode("/PATH"),这里需要把请求的URI的PATH部分进行UTF-8 编码。
       percentEncode(CanonicalizedQueryString) //您的规范化请求字符串。
      
    2. 按照 RFC2104 的定义,计算待签名字符串 StringToSign 的HMAC-SHA1值。示例使用的是Java Base64编码方法。

      Signature = Base64( HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) ) )
      

      说明 计算签名时,RFC2104规定的Key值是您的 AccessKeySecret 并加上与号( &),其ASCII值为38。

    3. 添加根据 RFC3986 规则编码后的参数 Signature 到规范化请求字符串URL中。

    示例 1. 参数拼接法

    以调用查询实例列表为例。假设您获得了 PublicKey=testid 以及 SecretKey=testsecret,签名流程如下所示:

    1. 构造规范化请求字符串。

      http://qvm.qiniuapi.com/v1/instance?page=1&page_size=30&code=ecs&public_key=testid&signature_method=HMAC-SHA1&signature_version=1.0&signature_nonce=402232001&timestamp=2018-12-11T03%3A36%3A52Z

    2. 构造待签名字符串 StringToSign。

      GET&%2Fv1%2Finstance&code%3Decs%26public_key%3Dtestid%26signature_method%3DHMAC-SHA1%26signature_nonce%3D3ee8c1b8-83d3-44af-a94f-4e0ad82fd6cf%26signature_version%3D1.0%26timestamp%3D2016-02-23T12%253A46%253A24Z

    3. 计算签名值。因为 SecretKey=testsecret,用于计算的Key为 testsecret&,计算得到的签名值为 RlwhSvt3UUmm9QewhcaR/Lc4Cwg= 。示例使用的是Java Base64编码方法。

    Signature = Base64( HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) ) )

    1. 添加 RFC3986 规则编码后的signature=RlwhSvt3UUmm9QewhcaR%2FLc4Cwg%3D 到 步骤1 的URL中。

      http://qvm.qiniuapi.com/v1/instance?page=1&page_size=30&code=ecs&public_key=testid&signature_method=HMAC-SHA1&signature_version=1.0&signature_nonce=402232001&timestamp=2018-12-11T03%3A36%3A52Z&signature=RlwhSvt3UUmm9QewhcaR%2FLc4Cwg%3D

    通过以上URL,您可以使用浏览器、curl或者wget等工具发起HTTP请求调用 查看云主机列表。

    示例 2. 编程语言法

    依然以调用 查看云主机列表为例。假设您获得了PublicKey=testid以及SecretKey=testsecret,并且假定所有请求参数放在一个Java Map<String, String> 对象里。

    1. 预定义编码方法。

      private static final String ENCODING = "UTF-8";
      private static String percentEncode(String value) throws UnsupportedEncodingException {
       return value != null ? URLEncoder.encode(value, ENCODING).replace("+", "%20").replace("*", "%2A").replace("%7E", "~") : null;
      }
      
    2. 预定义编码时间格式 Timestamp。参数 Timestamp 必须符合 ISO8601 规范,并需要使用UTC时间,时区为+0。

      private static final String ISO8601_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
      private static String formatIso8601Date(Date date) {
       SimpleDateFormat df = new SimpleDateFormat(ISO8601_DATE_FORMAT);
       df.setTimeZone(new SimpleTimeZone(0, "GMT"));
       return df.format(date);
      }
      
    3. 构造请求字符串。

      final String HTTP_METHOD = "GET";
      Map parameters = new HashMap(); // 输入请求参数
      parameters.put("code", "ecs");
      parameters.put("public_key", "testid");
      parameters.put("timestamp", formatIso8601Date(new Date()));
      parameters.put("signature_method", "HMAC-SHA1");
      parameters.put("signature_version", "1.0");
      parameters.put("signature_nonce", UUID.randomUUID().toString());
      String DescribeInstancePath = "/v1/instance";
      sortedKeys = parameters.keySet().toArray(new String[]{});
      Arrays.sort(sortedKeys);
      final String SEPARATOR = "&"; // 构造 stringToSign 字符串
      StringBuilder stringToSign = new StringBuilder();
      stringToSign.append(HTTP_METHOD).append(SEPARATOR);
      stringToSign.append(percentEncode(DescribeInstancePath)).append(SEPARATOR);
      StringBuilder canonicalizedQueryString = new StringBuilder();
      for(String key : sortedKeys) {
       // 这里注意编码 key 和 value
       canonicalizedQueryString.append("&")
           .append(percentEncode(key)).append("=")
           .append(percentEncode(parameters.get(key)));
      } // 这里注意编码 canonicalizedQueryString
      stringToSign.append(
       percentEncode(
           canonicalizedQueryString.toString().substring(1)
       )
      );
      
    4. 签名。因为 AccessKeySecret=testsecret,所以用于计算HMAC的Key为 testsecret&,计算得到的签名值为 RlwhSvt3UUmm9QewhcaR%2FLc4Cwg%3D。

      // 以下是一段计算签名的示例代码
      final String ALGORITHM = "HmacSHA1";
      final String ENCODING = "UTF-8";
      key = "testsecret&";
      Mac mac = Mac.getInstance(ALGORITHM);
      mac.init(new SecretKeySpec(key.getBytes(ENCODING), ALGORITHM));
      byte[] signData = mac.doFinal(stringToSign.getBytes(ENCODING));
      String signature = new String(Base64.encodeBase64(signData));
      

      增加签名参数后,按照 RFC3986 规则编码后的URL如下所示:
      http://qvm.qiniuapi.com/v1/instance?page=1&page_size=30&code=ecs&public_key=testid&signature_method=HMAC-SHA1&signature_version=1.0&signature_nonce=402232001&timestamp=2018-12-11T03%3A36%3A52Z&signature=RlwhSvt3UUmm9QewhcaR%2FLc4Cwg%3D

    5. 使用浏览器、curl或者wget等工具发送HTTP请求。

      {
       "RequestId":"f50c87b1-314b-4a40-bfb8-4089cb1f5478",
       "code":0,
       "error_message":"",
       "data":{
           "count":3,
           "instances":[
               {
                   "create_time":"2018-12-10T08:39:11.046Z",
                   "device_available":true,
                   "eip_address":{
                       "allocation_id":"",
                       "bandwidth":0,
                       "internet_charge_type":"",
                       "ip_address":"",
                       "is_support_dissociate":false
                   },
                   "expired_time":"2019-01-10T16:00:00Z",
                   "host_name":"iZ2ze5swka4isqxfbez6ynZ",
                   "image_id":"centos_7_04_64_20G_alibase_201701015.vhd",
                   "inner_ip_address":{
                       "ip_address":[
      
                       ]
                   },
                   "instance_charge_type":"PrePaid",
                   "instance_id":"i-2ze5swka4isqxfbez6yn",
                   "instance_name":"iZ2ze5swka4isqxfbez6ynZ",
                   "instance_type":"ecs.t5-lc2m1.nano",
                   "operation_locks":{
                       "lock_reason":[
      
                       ]
                   },
                   "region_id":"cn-beijing",
                   "security_group_ids":{
                       "security_group_id":[
                           "sg-2zebscni6kr6g9ipepgb"
                       ]
                   },
                   "serial_number":"710d0d68-d9c8-4202-bb24-709bcc7be7c9",
                   "status":"Running",
                   "vpc_attributes":{
                       "ip_address":{
                           "ip_address":[
                               "10.0.6.20"
                           ]
                       },
                       "nat_ip_address":"",
                       "vpc_id":"vpc-2zeg6trfszfjfcco59yby",
                       "vswitch_id":"vsw-2zeh4jr2v8mqxizorudc0"
                   },
                   "zone_id":"cn-beijing-f"
               },
               {
                   "create_time":"2018-12-10T10:21:31.903Z",
                   "device_available":true,
                   "eip_address":{
                       "allocation_id":"",
                       "bandwidth":0,
                       "internet_charge_type":"",
                       "ip_address":"",
                       "is_support_dissociate":false
                   },
                   "expired_time":"2019-01-10T16:00:00Z",
                   "host_name":"iZ2ze6brknpgbvhnyyxkbjZ",
                   "image_id":"centos_7_04_64_20G_alibase_201701015.vhd",
                   "inner_ip_address":{
                       "ip_address":[
      
                       ]
                   },
                   "instance_charge_type":"PrePaid",
                   "instance_id":"i-2ze6brknpgbvhnyyxkbj",
                   "instance_name":"iZ2ze6brknpgbvhnyyxkbjZ",
                   "instance_type":"ecs.t5-lc2m1.nano",
                   "operation_locks":{
                       "lock_reason":[
      
                       ]
                   },
                   "region_id":"cn-beijing",
                   "security_group_ids":{
                       "security_group_id":[
                           "sg-2zebscni6kr6g9ipepgb"
                       ]
                   },
                   "serial_number":"43824b0c-d68d-4450-82c4-fced726b54b9",
                   "status":"Running",
                   "vpc_attributes":{
                       "ip_address":{
                           "ip_address":[
                               "10.0.6.21"
                           ]
                       },
                       "nat_ip_address":"",
                       "vpc_id":"vpc-2zeg6trfszfjfcco59yby",
                       "vswitch_id":"vsw-2zeh4jr2v8mqxizorudc0"
                   },
                   "zone_id":"cn-beijing-f"
               },
               {
                   "create_time":"2018-12-10T10:22:30.426Z",
                   "device_available":true,
                   "eip_address":{
                       "allocation_id":"",
                       "bandwidth":0,
                       "internet_charge_type":"",
                       "ip_address":"",
                       "is_support_dissociate":false
                   },
                   "expired_time":"2019-01-10T16:00:00Z",
                   "host_name":"iZ2ze8zme64j8i9y4kxqr0Z",
                   "image_id":"centos_7_04_64_20G_alibase_201701015.vhd",
                   "inner_ip_address":{
                       "ip_address":[
      
                       ]
                   },
                   "instance_charge_type":"PrePaid",
                   "instance_id":"i-2ze8zme64j8i9y4kxqr0",
                   "instance_name":"iZ2ze8zme64j8i9y4kxqr0Z",
                   "instance_type":"ecs.t5-lc2m1.nano",
                   "operation_locks":{
                       "lock_reason":[
      
                       ]
                   },
                   "region_id":"cn-beijing",
                   "security_group_ids":{
                       "security_group_id":[
                           "sg-2zebscni6kr6g9ipepgb"
                       ]
                   },
                   "serial_number":"7690ac2f-74d0-42fc-aeed-bf155d55ec83",
                   "status":"Running",
                   "vpc_attributes":{
                       "ip_address":{
                           "ip_address":[
                               "10.0.6.22"
                           ]
                       },
                       "nat_ip_address":"",
                       "vpc_id":"vpc-2zeg6trfszfjfcco59yby",
                       "vswitch_id":"vsw-2zeh4jr2v8mqxizorudc0"
                   },
                   "zone_id":"cn-beijing-f"
               }
           ],
           "page":1,
           "page_size":30
       }
      }
      
    以上内容是否对您有帮助?
  • Icon free helper
    Close