百度智能云

All Product Document

          Reference

          Sample-Code

          Python Example

          Assume that the user uploads the last part of a file to the BOS cluster in Beijing with the UploadPart interface, whose content is Example.

          • Bucket name: test
          • Object key: myfolder/readme.txt
          • uploadId: a44cc9bab11cbd156984767aad637851
          • partNumber: 9
          • Access Key ID: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
          • Secret Access Key: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
          • Time: Beijing time at 16:23:49 on April 27, 2015 (translated to UTC time: 8:23:49 on April 27, 2015)

          The HTTP request is as follows:

          PUT /test/myfolder/readme.txt?partNumber=9&uploadId=a44cc9bab11cbd156984767aad637851 HTTP/1.1
          Host: bj.bcebos.com
          Date: Mon, 27 Apr 2015 16:23:49 +0800
          Content-Type: text/plain
          Content-Length: 8
          Content-Md5: NFzcPqhviddjRNnSOGo4rw==
          x-bce-date: 2015-04-27T08:23:49Z
          
          Example

          Users can fill in the fields in the following functions according to the above HTTP request.

           if __name__ == "__main__":
               credentials = BceCredentials("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
               http_method = "PUT"
               path = "/test/myfolder/readme.txt"
               headers = {"host": "bj.bcebos.com",
                          "content-length": 8,
                          "content-md5": "NFzcPqhviddjRNnSOGo4rw==",
                          "content-type":"text/plain",
                          "x-bce-date": "2015-04-27T08:23:49Z"}
               params = {"partNumber": 9,
                         "uploadId": "a44cc9bab11cbd156984767aad637851"}
               timestamp = 1430123029
               result = sign(credentials, http_method, path, headers, params, timestamp)
               print result

          Please click here for the full code

          The complete code is as follows:

           # -*- coding: UTF-8 -*-
           import hashlib
           import hmac
           import string
           import datetime
           
           
           AUTHORIZATION = "authorization"
           BCE_PREFIX = "x-bce-"
           DEFAULT_ENCODING = 'UTF-8'
           
           
           # Save AK/SK classes 
           class BceCredentials(object):
               def __init__(self, access_key_id, secret_access_key):
                   self.access_key_id = access_key_id
                   self.secret_access_key = secret_access_key
           
           
           # According to RFC 3986, except: 
           #    1. Uppercase and lowercase English characters 
           #    2. Arabic numerals 
           #    3. Dot '.', tilde '~', minus sign '-', and underscore '_' 
           # The others need to be encoded 
           RESERVED_CHAR_SET = set(string.ascii_letters + string.digits + '.~-_')
           def get_normalized_char(i):
               char = chr(i)
               if char in RESERVED_CHAR_SET:
                   return char
               else:
                   return '%%%02X' % i
           NORMALIZED_CHAR_LIST = [get_normalized_char(i) for i in range(256)]
           
           
           # Normalize String
           def normalize_string(in_str, encoding_slash=True):
               if in_str is None:
                   return ''
           
               # If the input is unicode, it shall be encoded by UTF8 first and then encoded 
               in_str = in_str.encode(DEFAULT_ENCODING) if isinstance(in_str, unicode) else str(in_str)
           
               # When generating a canonical URI. No need to encode the slash '/', otherwise 
               if encoding_slash:
                   encode_f = lambda c: NORMALIZED_CHAR_LIST[ord(c)]
               else:
                   # Only when generating canonical URIs. No need to encode the slash '/' 
                   encode_f = lambda c: NORMALIZED_CHAR_LIST[ord(c)] if c != '/' else c
           
               # Encoding according to RFC 3986 
               return ''.join([encode_f(ch) for ch in in_str])
           
           
           # Generate canonical timestamp 
           def get_canonical_time(timestamp=0):
               # Return the current time when called without any parameters 
               if timestamp == 0:
                   utctime = datetime.datetime.utcnow()
               else:
                   utctime = datetime.datetime.utcfromtimestamp(timestamp)
           
               # Timestamp format: [year]-[month]-[day]T[hour]:[minute]:[second]Z 
               return "%04d-%02d-%02dT%02d:%02d:%02dZ" % (
                   utctime.year, utctime.month, utctime.day,
                   utctime.hour, utctime.minute, utctime.second)
           
           
           # Generate normalized URI 
           def get_canonical_uri(path):
               # The format of a normalized URI is: / {bucket}/{object}, and encode all characters except the slash "/" 
               return normalize_string(path, False)
           
           
           # Generate normalized query string 
           def get_canonical_querystring(params):
               if params is None:
                   return ''
           
               # In addition to authorization, all query strings are added to the encoding 
               result = ['%s=%s' % (normalize_string(k), normalize_string(v)) for k, v in params.items() if k.lower != AUTHORIZATION]
           
               # Sort in lexicographic order 
               result.sort () 
           
               # Concatenate all strings with&symbol and return 
               return '&'.join(result)
           
           
           # Generate normalized header 
           def get_canonical_headers(headers, headers_to_sign=None):
               headers = headers or {}
           
               # If header_to_sign is not specified, use by default: 
               #   1.host
               #   2.content-md5
               #   3.content-length
               #   4.content-type
               #   5. All header items that start with x-bce- 
               # Generate normalized header 
               if headers_to_sign is None or len(headers_to_sign) == 0:
                   headers_to_sign = {"host", "content-md5", "content-length", "content-type"}
           
               # For the key in the header, it needs to be converted to lowercase after removing the white space before and after 
               # For the value in the header, remove the white space before and after the conversion to str 
               f = lambda (key, value): (key.strip().lower(), str(value).strip())
           
               result = []
               for k, v in map(f, headers.iteritems()):
                   # In any case, header items starting with x-bce- need to be added to the canonical header 
                   if k.startswith(BCE_PREFIX) or k in headers_to_sign:
                       result.append("%s:%s" % (normalize_string(k), normalize_string(v)))
           
               # Sort in lexicographic order 
               result.sort () 
           
               # Concatenate all strings with\n symbol and return 
               return '\n'.join(result)
           
           
           # Signature Master Algorithm 
           def sign(credentials, http_method, path, headers, params,
                    timestamp=0, expiration_in_seconds=1800, headers_to_sign=None):
               headers = headers or {}
               params = params or {}
           
               # 1. Generate sign key 
               # 1.1. Generate auth-string in the format: bce-auth-v1/{accessKeyId}/{timestamp}/{expirationPeriodInSeconds} 
               sign_key_info = 'bce-auth-v1/%s/%s/%d' % (
                   credentials.access_key_id,
                   get_canonical_time(timestamp),
                   expiration_in_seconds)
               # 1.2. Use auth-string plus SK and SHA-256 to generate sign key 
               sign_key = hmac.new(
                   credentials.secret_access_key,
                   sign_key_info,
                   hashlib.sha256).hexdigest()
           
               # 2. Generate normalized uri 
               canonical_uri = get_canonical_uri(path)
           
               # 3. Generate a normalized query string 
               canonical_querystring = get_canonical_querystring(params)
           
               # 4. Generate a normalized header 
               canonical_headers = get_canonical_headers(headers, headers_to_sign)
           
               # 5. Use '\ n' to connect the HTTP METHOD with the results in 2, 3, 4 into a large string 
               string_to_sign = '\n'.join(
                   [http_method, canonical_uri, canonical_querystring, canonical_headers])
           
               # 6. Use the signature string generated in 5 and the sign key generated in 1 to generate a signature result using the SHA-256 algorithm 
               sign_result = hmac.new(sign_key, string_to_sign, hashlib.sha256).hexdigest()
           
               # 7. Stitch the final signature result string 
               if headers_to_sign:
                   # Specify header to sign 
                   result = '%s/%s/%s' % (sign_key_info, ';'.join(headers_to_sign), sign_result)
               else:
                   # Default signature result string without specifying header to sign 
                   result = '%s//%s' % (sign_key_info, sign_result)
           
               return result
           
           if __name__ == "__main__":
               credentials = BceCredentials("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
               http_method = "PUT"
               path = "/test/myfolder/readme.txt"
               headers = {"host": "bj.bcebos.com",
                          "content-length": 8,
                          "content-md5": "NFzcPqhviddjRNnSOGo4rw==",
                          "content-type":"text/plain",
                          "x-bce-date": "2015-04-27T08:23:49Z"}
               params = {"partNumber": 9,
                         "uploadId": "a44cc9bab11cbd156984767aad637851"}
               timestamp = 1430123029
               result = sign(credentials, http_method, path, headers, params, timestamp)
               print result

          Php Example

          Assume that the user uploads the last part of a file to the BOS cluster in Beijing with the UploadPart interface, whose content is Example.

          • Bucket name: test
          • Object key: myfolder/readme.txt
          • uploadId: a44cc9bab11cbd156984767aad637851
          • partNumber: 9
          • Access Key ID: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
          • Secret Access Key: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
          • Time: Beijing time at 16:23:49 on April 27, 2015 (translated to UTC time: 8:23:49 on April 27, 2015)

          The HTTP request is as follows:

          PUT /test/myfolder/readme.txt?partNumber=9&uploadId=a44cc9bab11cbd156984767aad637851 HTTP/1.1
          Host: bj.bcebos.com
          Date: Mon, 27 Apr 2015 16:23:49 +0800
          Content-Type: text/plain
          Content-Length: 8
          Content-Md5: NFzcPqhviddjRNnSOGo4rw==
          x-bce-date: 2015-04-27T08:23:49Z
          
          Example

          Signing sample code

          $signer = new SampleSigner();
          $credentials = array("ak" => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","sk" => "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
          $httpMethod = "PUT";
          $path = "/v1/test/myfolder/readme.txt";
          $headers = array("Host" => "bj.bcebos.com",
                          "Content-Length" => 8,
                          "Content-MD5" => "NFzcPqhviddjRNnSOGo4rw==",
                          "Content-Type" => "text/plain",
                          "x-bce-date" => "2015-04-27T08:23:49Z");
          $params = array("partNumber" => 9, "uploadId" => "a44cc9bab11cbd156984767aad637851");
          $timestamp = new \DateTime();
          $timestamp->setTimestamp(1430123029);
          $options = array(SignOption::TIMESTAMP => $timestamp);
          $ret = $signer->sign($credentials, $httpMethod, $path, $headers, $params, $options);
          print $ret;

          Please click here for the full code

          The complete code is as follows:

          <?php
          /*
          * Copyright (c) 2014 Baidu.com, Inc. All Rights Reserved
          *
          * Licensed under the Apache License, Version 2.0 (the "License"); you may not
          * use this file except in compliance with the License. You may obtain a copy of
          * the License at
          *
          * Http://www.apache.org/licenses/LICENSE-2.0
          *
          * Unless required by applicable law or agreed to in writing, software
          * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
          * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
          * License for the specific language governing privileges and limitations under
          * the License.
          */
          
          namespace BaiduBce\Auth;
          
          class SignOption
          {
              const EXPIRATION_IN_SECONDS = 'expirationInSeconds';
          
              const HEADERS_TO_SIGN = 'headersToSign';
          
              const TIMESTAMP = 'timestamp';
          
              const DEFAULT_EXPIRATION_IN_SECONDS = 1800;
          
              const MIN_EXPIRATION_IN_SECONDS = 300;
          
              const MAX_EXPIRATION_IN_SECONDS = 129600;
          }
          
          class HttpUtil
          {
              // According to RFC 3986, except: 
              //    1. Uppercase and lowercase English characters 
              //    2. Arabic numerals 
              //    3. Dot '.', tilde '~', minus sign '-', and underscore '_' 
              // The others need to be encoded 
              public static $PERCENT_ENCODED_STRINGS;
          
              // Fill the encoded array 
              public static function __init()
              {
                  HttpUtil::$PERCENT_ENCODED_STRINGS = array();
                  for ($i = 0; $i < 256; ++$i) {
                      HttpUtil::$PERCENT_ENCODED_STRINGS[$i] = sprintf("%%%02X", $i);
                  }
          
                  // a-z does not encode 
                  foreach (range('a', 'z') as $ch) {
                      HttpUtil::$PERCENT_ENCODED_STRINGS[ord($ch)] = $ch;
                  }
          
                  // A-Z does not encode 
                  foreach (range('A', 'Z') as $ch) {
                      HttpUtil::$PERCENT_ENCODED_STRINGS[ord($ch)] = $ch;
                  }
          
                  // 0-9 does not encode 
                  foreach (range('0', '9') as $ch) {
                      HttpUtil::$PERCENT_ENCODED_STRINGS[ord($ch)] = $ch;
                  }
          
                  // The following 4 characters are not encoded 
                  HttpUtil::$PERCENT_ENCODED_STRINGS[ord('-')] = '-';
                  HttpUtil::$PERCENT_ENCODED_STRINGS[ord('.')] = '.';
                  HttpUtil::$PERCENT_ENCODED_STRINGS[ord('_')] = '_';
                  HttpUtil::$PERCENT_ENCODED_STRINGS[ord('~')] = '~';
              }
          
              // Can't encode '/' in uri encoding 
              public static function urlEncodeExceptSlash($path)
              {
                  return str_replace("%2F", "/", HttpUtil::urlEncode($path));
              }
          
              // use encoding array encoding 
              public static function urlEncode($value)
              {
                  $result = '';
                  for ($i = 0; $i < strlen($value); ++$i) {
                      $result .= HttpUtil::$PERCENT_ENCODED_STRINGS[ord($value[$i])];
                  }
                  return $result;
              }
          
              // Generate standardized QueryString 
              public static function getCanonicalQueryString(array $parameters)
              {
                  // No parameters, return empty string directly 
                  if (count($parameters) == 0) {
                      return '';
                  }
          
                  $parameterStrings = array();
                  foreach ($parameters as $k => $v) {
                      // Skip the Authorization field 
                      if (strcasecmp('Authorization', $k) == 0) {
                          continue;
                      }
                      if (!isset($k)) {
                          throw new \InvalidArgumentException(
                              "parameter key should not be null"
                          );
                      }
                      if (isset($v)) {
                          // For the value, put it on both sides of = after encoding 
                          $parameterStrings[] = HttpUtil::urlEncode($k)
                              . '=' . HttpUtil::urlEncode((string) $v);
                      } else {
                          // For those without a value, just encode the key and place it to the left of =, leaving the right blank 
                          $parameterStrings[] = HttpUtil::urlEncode($k) . '=';
                      }
                  }
                  // Sort in lexicographic order 
                  sort($parameterStrings);
          
                  // connect them using the '&' symbol 
                  return implode('&', $parameterStrings);
              }
          
              // Generate standardized uri 
              public static function getCanonicalURIPath($path)
              {
                  // The empty path is set to '/' 
                  if (empty($path)) {
                      return '/';
                  } else {
                      // All uri must start with '/' 
                      if ($path[0] == '/') {
                          return HttpUtil::urlEncodeExceptSlash($path);
                      } else {
                          return '/' . HttpUtil::urlEncodeExceptSlash($path);
                      }
                  }
              }
          
              // Generate a standardized http request header string 
              public static function getCanonicalHeaders($headers)
              {
                  // If no headers, return empty string 
                  if (count($headers) == 0) {
                      return '';
                  }
          
                  $headerStrings = array();
                  foreach ($headers as $k => $v) {
                      // Skip the null key 
                      if ($k === null) {
                          continue;
                      }
                      // If value is null, the assignment is an empty string 
                      if ($v === null) {
                          $v = '';
                      }
                      // encode after trim, then connect with ':' 
                      $headerStrings[] = HttpUtil::urlEncode(strtolower(trim($k))) . ':' . HttpUtil::urlEncode(trim($v));
                  }
                  // Sort in lexicographic order 
                  sort($headerStrings);
          
                  // connect them with '\ n' 
                  return implode("\n", $headerStrings);
              }
          
          }
          HttpUtil::__init();
          
          
          class SampleSigner
          {
          
              const BCE_AUTH_VERSION = "bce-auth-v1";
              const BCE_PREFIX = 'x-bce-';
          
              // If headersToSign is not specified, the default http header is signed, including: 
              //    1.host
              //    2.content-length
              //    3.content-type
              //    4.content-md5
              public static $defaultHeadersToSign;
          
              public static function  __init()
              {
                  SampleSigner::$defaultHeadersToSign = array(
                      "host",
                      "content-length",
                      "content-type",
                      "content-md5",
                  );
              }
          
              // Signature function 
              public function sign(
                  array $credentials,
                  $httpMethod,
                  $path,
                  $headers,
                  $params,
                  $options = array()
              ) {
                  // Set signature validity time 
                  if (!isset($options[SignOption::EXPIRATION_IN_SECONDS])) {
                      // The default value is 1800 seconds 
                      $expirationInSeconds = SignOption::DEFAULT_EXPIRATION_IN_SECONDS;
                  } else {
                      $expirationInSeconds = $options[SignOption::EXPIRATION_IN_SECONDS];
                  }
          
                  // Parse ak sk 
                  $accessKeyId = $credentials['ak'];
                  $secretAccessKey = $credentials['sk'];
          
                  // Set the timestamp, Note: If you specify the timestamp yourself, it needs to be UTC time 
                  if (!isset($options[SignOption::TIMESTAMP])) {
                      // default value current time 
                      $timestamp = new \DateTime();
                  } else {
                      $timestamp = $options[SignOption::TIMESTAMP];
                  }
                  $timestamp->setTimezone(new \DateTimeZone("UTC"));
          
                  // Generate authString 
                  $authString = SampleSigner::BCE_AUTH_VERSION . '/' . $accessKeyId . '/'
                      . $timestamp->format("Y-m-d\TH:i:s\Z") . '/' . $expirationInSeconds;
          
                  // Generate signKey using sk and authString 
                  $signingKey = hash_hmac('sha256', $authString, $secretAccessKey);
          
                  // Generate a standardized URI 
                  $canonicalURI = HttpUtil::getCanonicalURIPath($path);
          
                  // Generate standardized QueryString 
                  $canonicalQueryString = HttpUtil::getCanonicalQueryString($params);
          
                  // fill headersToSign, which indicates which headers participate in the signature 
                  $headersToSignOption = null;
                  if (isset($options[SignOption::HEADERS_TO_SIGN])) {
                      $headersToSignOption = $options[SignOption::HEADERS_TO_SIGN];
                  }
                  
                  $headersToSign = SampleSigner::getHeadersToSign($headers, $headersToSignOption);
                  
                  // Generate standardized header 
                  $canonicalHeader = HttpUtil::getCanonicalHeaders($headersToSign);
                  
                  $headersToSign = array_keys($headersToSign);
                  sort($headersToSign);
                  // Fix headersToSign and connect with ';' 
                  $signedHeaders = '';
                  if ($headersToSignOption !== null) {
                      $signedHeaders = strtolower(
                          trim(implode(";", $headersToSign))
                      );
                  }
          
                  // make up a standard request string 
                  $canonicalRequest = "$httpMethod\n$canonicalURI\n"
                      . "$canonicalQueryString\n$canonicalHeader";
          
                  // Use signKey and standard request string to complete the signature 
                  $signature = hash_hmac('sha256', $canonicalRequest, $signingKey);
          
                  // make up the final signature string 
                  $authorizationHeader = "$authString/$signedHeaders/$signature";
          
                  return $authorizationHeader;
              }
          
              /** Filter headers that should participate in signature based on headsToSign 
               * 
               * @param $headers array
               * @param $headersToSign array
               * @return array
               */
              public static function getHeadersToSign($headers, $headersToSign)
              {
          
                  $ret = array();
                  if ($headersToSign !== null) {
                      $tmp = array();
          
                      // Handle the keys of the headers: Remove whitespace before and after and convert to lowercase 
                      foreach ($headersToSign as $header) {
                          $tmp[] = strtolower(trim($header));
                      }
                      $headersToSign = $tmp;
                  }
                  foreach ($headers as $k => $v) {
                      if (trim((string) $v) !== '') {
                          if ($headersToSign !== null) {
                              // Preprocessing headersToSign: Remove whitespace before and after and convert to lowercase 
                              if (in_array(strtolower(trim($k)), $headersToSign)) {
                                  $ret[$k] = $v;
                              }
                          } else {
                              // If there is no headersToSign, the headers are selected according to the default rules 
                              if (SampleSigner::isDefaultHeaderToSign($k, $headersToSign)) {
                                  $ret[$k] = $v;
                              }
                          }
                      }
                  }
                  return $ret;
              }
          
              /**
               * Check if the header is signed by default: 
               * 1. it is one of host, content-type, content-md5, content-length 
               * 2. Start with x-bce 
               *
               * @param $header string
               * @return bool
               */
              public static function isDefaultHeaderToSign($header)
              {
                  $header = strtolower(trim($header));
                  if (in_array($header, SampleSigner::$defaultHeadersToSign)) {
                      return true;
                  }
                  $prefix = substr($header, 0, strlen(SampleSigner::BCE_PREFIX));
                  if ($prefix === SampleSigner::BCE_PREFIX) {
                      return true;
                  } else {
                      return false;
                  }
              }
          }
          
          SampleSigner::__init();
          
          
          
          // Sign the sample code 
          $signer = new SampleSigner();
          $credentials = array("ak" => "0b0f67dfb88244b289b72b142befad0c","sk" => "bad522c2126a4618a8125f4b6cf6356f");
          $httpMethod = "PUT";
          $path = "/v1/test/myfolder/readme.txt";
          $headers = array("Host" => "bj.bcebos.com",
                          "Content-Length" => 8,
                          "Content-MD5" => "0a52730597fb4ffa01fc117d9e71e3a9",
                          "Content-Type" => "text/plain",
                          "x-bce-date" => "2015-04-27T08:23:49Z");
          $params = array("partNumber" => 9, "uploadId" => "VXBsb2FkIElpZS5tMnRzIHVwbG9hZA");
          date_default_timezone_set("PRC");
          $timestamp = new \DateTime();
          $timestamp->setTimestamp(1430123029);
          $options = array(SignOption::TIMESTAMP => $timestamp);
          // $options = array(SignOption::TIMESTAMP => $timestamp, SignOption::HEADERS_TO_SIGN => array("Content-Type", "Host", "x-bce-date"));
          $ret = $signer->sign($credentials, $httpMethod, $path, $headers, $params, $options);
          print $ret;

          Java Example

          Users can refer to the following code to learn more about Baidu AI Cloud API authentication mechanism.

          Code download path: https://github.com/baidubce/bce-sdk-java/blob/master/src/main/java/com/baidubce/auth/BceV1Signer.java

          Note: The API authentication sample code of the Android language is consistent with the Java sample code.

          /*
           * Copyright (c) 2014 Baidu.com, Inc. All Rights Reserved
           *
           * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
           * the License. You may obtain a copy of the License at
           *
           * http://www.apache.org/licenses/LICENSE-2.0
           *
           * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
           * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
           * specific language governing privileges and limitations under the License.
           */
          
          package com.baidubce.auth;
          
          import com.baidubce.BceClientException;
          import com.baidubce.http.Headers;
          import com.baidubce.internal.InternalRequest;
          import com.baidubce.util.DateUtils;
          import com.baidubce.util.HttpUtils;
          import com.google.common.base.Joiner;
          import com.google.common.collect.Lists;
          import com.google.common.collect.Maps;
          import com.google.common.collect.Sets;
          
          import org.apache.commons.codec.binary.Hex;
          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;
          
          import javax.crypto.Mac;
          import javax.crypto.spec.SecretKeySpec;
          
          import java.nio.charset.Charset;
          import java.util.Collections;
          import java.util.Date;
          import java.util.List;
          import java.util.Map;
          import java.util.Set;
          import java.util.SortedMap;
          
          import static com.google.common.base.Preconditions.checkNotNull;
          
          /**
           * The V1 implementation of Signer with the BCE signing protocol.
           */
          public class BceV1Signer implements Signer {
          
              private static final Logger logger = LoggerFactory.getLogger(BceV1Signer.class);
          
              private static final String BCE_AUTH_VERSION = "bce-auth-v1";
              private static final String DEFAULT_ENCODING = "UTF-8";
              private static final Charset UTF8 = Charset.forName(DEFAULT_ENCODING);
          
              // Default headers to sign with the BCE signing protocol.
              private static final Set<String> defaultHeadersToSign = Sets.newHashSet();
              private static final Joiner headerJoiner = Joiner.on('\n');
              private static final Joiner signedHeaderStringJoiner = Joiner.on(';');
          
              static {
                  BceV1Signer.defaultHeadersToSign.add(Headers.HOST.toLowerCase());
                  BceV1Signer.defaultHeadersToSign.add(Headers.CONTENT_LENGTH.toLowerCase());
                  BceV1Signer.defaultHeadersToSign.add(Headers.CONTENT_TYPE.toLowerCase());
                  BceV1Signer.defaultHeadersToSign.add(Headers.CONTENT_MD5.toLowerCase());
              }
          
              /**
               * @see com.baidubce.auth.Signer#sign(InternalRequest, BceCredentials)
               */
              @Override
              public void sign(InternalRequest request, BceCredentials credentials) {
                  this.sign(request, credentials, null);
              }
          
              /**
               * Sign the given request with the given set of credentials. Modifies the passed-in request to apply the signature.
               *
               * @param request     the request to sign.
               * @param credentials the credentials to sign the request with.
               * @param options     the options for signing.
               */
              @Override
              public void sign(InternalRequest request, BceCredentials credentials, SignOptions options) {
                  checkNotNull(request, "request should not be null.");
          
                  if (credentials == null) {
                      return;
                  }
          
                  if (options == null) {
                      if (request.getSignOptions() != null) {
                          options = request.getSignOptions();
                      } else {
                          options = SignOptions.DEFAULT;
                      }
                  }
          
                  String accessKeyId = credentials.getAccessKeyId();
                  String secretAccessKey = credentials.getSecretKey();
          
                  request.addHeader(Headers.HOST, HttpUtils.generateHostHeader(request.getUri()));
          
                  Date timestamp = options.getTimestamp();
                  if (timestamp == null) {
                      timestamp = new Date();
                  }
          
                  String authString =
                          BceV1Signer.BCE_AUTH_VERSION + "/" + accessKeyId + "/"
                                  + DateUtils.formatAlternateIso8601Date(timestamp) + "/" + options.getExpirationInSeconds();
          
                  String signingKey = this.sha256Hex(secretAccessKey, authString);
                  // Formatting the URL with signing protocol.
                  String canonicalURI = this.getCanonicalURIPath(request.getUri().getPath());
                  // Formatting the query string with signing protocol.
                  String canonicalQueryString = HttpUtils.getCanonicalQueryString(request.getParameters(), true);
                  // Sorted the headers should be signed from the request.
                  SortedMap<String, String> headersToSign =
                          this.getHeadersToSign(request.getHeaders(), options.getHeadersToSign());
                  // Formatting the headers from the request based on signing protocol.
                  String canonicalHeader = this.getCanonicalHeaders(headersToSign);
                  String signedHeaders = "";
                  if (options.getHeadersToSign() != null) {
                      signedHeaders = BceV1Signer.signedHeaderStringJoiner.join(headersToSign.keySet());
                      signedHeaders = signedHeaders.trim().toLowerCase();
                  }
          
                  String canonicalRequest =
                          request.getHttpMethod() + "\n" + canonicalURI + "\n" + canonicalQueryString + "\n" + canonicalHeader;
          
                  // Signing the canonical request using key with sha-256 algorithm.
                  String signature = this.sha256Hex(signingKey, canonicalRequest);
          
                  String authorizationHeader = authString + "/" + signedHeaders + "/" + signature;
          
                  logger.debug("CanonicalRequest:{}\tAuthorization:{}", canonicalRequest.replace("\n", "[\\n]"),
                          authorizationHeader);
          
                  request.addHeader(Headers.AUTHORIZATION, authorizationHeader);
              }
          
              private String getCanonicalURIPath(String path) {
                  if (path == null) {
                      return "/";
                  } else if (path.startsWith("/")) {
                      return HttpUtils.normalizePath(path);
                  } else {
                      return "/" + HttpUtils.normalizePath(path);
                  }
              }
          
              private String getCanonicalHeaders(SortedMap<String, String> headers) {
                  if (headers.isEmpty()) {
                      return "";
                  }
          
                  List<String> headerStrings = Lists.newArrayList();
                  for (Map.Entry<String, String> entry : headers.entrySet()) {
                      String key = entry.getKey();
                      if (key == null) {
                          continue;
                      }
                      String value = entry.getValue();
                      if (value == null) {
                          value = "";
                      }
                      headerStrings.add(HttpUtils.normalize(key.trim().toLowerCase()) + ':' + HttpUtils.normalize(value.trim()));
                  }
                  Collections.sort(headerStrings);
          
                  return headerJoiner.join(headerStrings);
              }
          
              private SortedMap<String, String> getHeadersToSign(Map<String, String> headers, Set<String> headersToSign) {
                  SortedMap<String, String> ret = Maps.newTreeMap();
                  if (headersToSign != null) {
                      Set<String> tempSet = Sets.newHashSet();
                      for (String header : headersToSign) {
                          tempSet.add(header.trim().toLowerCase());
                      }
                      headersToSign = tempSet;
                  }
                  for (Map.Entry<String, String> entry : headers.entrySet()) {
                      String key = entry.getKey();
                      if (entry.getValue() != null&&!entry.getValue().isEmpty()) {
                          if ((headersToSign == null&&this.isDefaultHeaderToSign(key))
                                  || (headersToSign != null&&headersToSign.contains(key.toLowerCase())
                                         &&!Headers.AUTHORIZATION.equalsIgnoreCase(key))) {
                              ret.put(key, entry.getValue());
                          }
                      }
                  }
                  return ret;
              }
          
              private boolean isDefaultHeaderToSign(String header) {
                  header = header.trim().toLowerCase();
                  return header.startsWith(Headers.BCE_PREFIX) || defaultHeadersToSign.contains(header);
              }
          
              private String sha256Hex(String signingKey, String stringToSign) {
                  try {
                      Mac mac = Mac.getInstance("HmacSHA256");
                      mac.init(new SecretKeySpec(signingKey.getBytes(UTF8), "HmacSHA256"));
                      return new String(Hex.encodeHex(mac.doFinal(stringToSign.getBytes(UTF8))));
                  } catch (Exception e) {
                      throw new BceClientException("Fail to generate the signature", e);
                  }
              }
          
          }

          Javascript Example

          Users can refer to the following code to learn more about Baidu AI Cloud API authentication mechanism in Javascript language.

          Code download path: https://github.com/baidubce/bce-sdk-js/blob/master/test/sdk/auth.spec.js

          var Auth = require('@baiducloud/sdk').Auth;
          
          
              var auth = new Auth('my_ak', 'my_sk');
          
              var method = 'PUT';
              var uri = '/v1/bucket/object1';
              var params = {
                  A: null,
                  b: '',
                  C: 'd'
              };
              var headers = {
                  'Host': 'bce.baidu.com',
                  'abc': '123',
                  'x-bce-meta-key1': 'ABC'
              };
          
              var signature = auth.generateAuthorization(method, uri, params, headers, 1402639056);
              expect(signature).to.eql('bce-auth-v1/my_ak/2014-06-13T05:57:36Z/1800/host;x-bce-meta-key1/'
                                       + '80c9672aca2ea9af4bb40b9a8ff458d72df94e97d550840727f3a929af271d25');
          
              signature = auth.generateAuthorization(method, uri, params, headers, 1402639056, 1800);
              expect(signature).to.eql('bce-auth-v1/my_ak/2014-06-13T05:57:36Z/1800/host;'
                                        + 'x-bce-meta-key1/80c9672aca2ea9af4bb40b9a8ff458d72'
                                        + 'df94e97d550840727f3a929af271d25');
          
              method = 'DELETE';
              uri = '/v1/test-bucket1361199862';
              params = {};
              headers = {
                  'Content-Type': 'application/json; charset=utf-8',
                  'Content-Length': 0,
                  'User-Agent': 'This is the user-agent'
              };
              signature = auth.generateAuthorization(method, uri, params, headers, 1402639056, 1800);
              expect(signature).to.eql('bce-auth-v1/my_ak/2014-06-13T05:57:36Z/1800/'
                                        + 'content-length;content-type/'
                                        + 'c9386b15d585960ae5e6972f73ed92a9a682dc81025480ba5b41206d3e489822');

          C # Example

          Users can refer to the following code to learn more about Baidu AI Cloud API authentication mechanism in C # language. Complete example code:

          using BaiduBce;
          using BaiduBce.Auth;
          using BaiduBce.Services.Bos;
          using BaiduBce.Services.Bos.Model;
          using BaiduBce.Services.Sts;
          using System;
          using System.Collections.Generic;
          using System.Collections.Specialized;
          using System.Diagnostics;
          using System.Globalization;
          using System.IO;
          using System.Net;
          using System.Security.Cryptography;
          using System.Text;
          using System.Web;
          
          namespace BOSTest
          {
              class Program
              {
                  static string UriEncode(string input, bool encodeSlash = false)
                  {
                      StringBuilder builder = new StringBuilder();
                      foreach (byte b in Encoding.UTF8.GetBytes(input))
                      {
                          if ((b >= 'a'&&b <= 'z') || (b >= 'A'&&b <= 'Z') || (b >= '0'&&b <= '9') || b == '_' || b == '-' || b == '~' || b == '.')
                          {
                              builder.Append((char)b);
                          }
                          else if (b == '/')
                          {
                              if (encodeSlash)
                              {
                                  builder.Append("%2F");
                              }
                              else
                              {
                                  builder.Append((char)b);
                              }
                          }
                          else
                          {
                              builder.Append('%').Append(b.ToString("X2"));
                          }
                      }
                      return builder.ToString();
                  }
          
                  static string Hex(byte[] data)
                  {
                      var sb = new StringBuilder();
                      foreach (var b in data)
                      {
                          sb.Append(b.ToString("x2"));
                      }
                      return sb.ToString();
                  }
          
                  static string CanonicalRequest(HttpWebRequest req)
                  {
                      Uri uri = req.RequestUri;
                      StringBuilder canonicalReq = new StringBuilder();
                      canonicalReq.Append(req.Method).Append("\n").Append(UriEncode(Uri.UnescapeDataString(uri.AbsolutePath))).Append("\n");
          
                      var parameters = HttpUtility.ParseQueryString(uri.Query);
                      List<string> parameterStrings = new List<string>();
                      foreach (KeyValuePair<string, string> entry in parameters)
                      {
                          parameterStrings.Add(UriEncode(entry.Key) + '=' + UriEncode(entry.Value));
                      }
                      parameterStrings.Sort();
                      canonicalReq.Append(string.Join("&", parameterStrings.ToArray())).Append("\n");
          
                      string host = uri.Host;
                      if (!(uri.Scheme == "https"&&uri.Port == 443)&&!(uri.Scheme == "http"&&uri.Port == 80))
                      {
                          host += ":" + uri.Port;
                      }
                      canonicalReq.Append("host:" + UriEncode(host));
                      return canonicalReq.ToString();
                  }
          
                  static void Main(string[] args)
                  {
                      string bucket = "mybucket";
                      string key = "myfile";
                      string ak = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
                      string sk = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
                      DateTime now = DateTime.Now;
                      int expirationInSeconds = 1200;
          
                      HttpWebRequest req = WebRequest.Create("http://bj.bcebos.com/" + bucket + "/" + key) as HttpWebRequest;
                      Uri uri = req.RequestUri;
                      req.Method = "GET";
          
                      string signDate = now.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssK");
                      Console.WriteLine(signDate);
                      string authString = "bce-auth-v1/" + ak + "/" + signDate + "/" + expirationInSeconds;
                      string signingKey = Hex(new HMACSHA256(Encoding.UTF8.GetBytes(sk)).ComputeHash(Encoding.UTF8.GetBytes(authString)));
                      Console.WriteLine(signingKey);
          
                      string canonicalRequestString = CanonicalRequest(req);
                      Console.WriteLine(canonicalRequestString);
          
                      string signature = Hex(new HMACSHA256(Encoding.UTF8.GetBytes(signingKey)).ComputeHash(Encoding.UTF8.GetBytes(canonicalRequestString)));
                      string authorization = authString + "/host/" + signature;
                      Console.WriteLine(authorization);
          
                      req.Headers.Add("x-bce-date", signDate);
                      req.Headers.Add(HttpRequestHeader.Authorization, authorization);
          
                      HttpWebResponse res;
                      string message = "";
                      try
                      {
                          res = req.GetResponse() as HttpWebResponse;
                      }
                      catch (WebException e)
                      {
                          res = e.Response as HttpWebResponse;
                          message = new StreamReader(res.GetResponseStream()).ReadToEnd();
                      }
                      Console.WriteLine((int)res.StatusCode);
                      Console.WriteLine(res.Headers);
                      Console.WriteLine(message);
                      Console.ReadLine();
                  }
              }
          }

          iOS Example

          Users can refer to the following code to learn more about Baidu AI Cloud API authentication mechanism for iOS. The code contains two types of class code and calling code, the complete code address: Code. For the calling code, please see:

          #import <XCTest/XCTest.h>
          #import "BDCloudSigner.h"
          
          @interface UT : XCTestCase
          @end
          
          @implementation UT
          
          id<BDCloudSigner> createSigner() {
              BDCloudCredentials* credentials = [BDCloudCredentials new];
              credentials.accessKey = @"<access key>";
              credentials.secretKey = @"<secret key>";
          
              id<BDCloudSigner> signer = [[BDCloudAKSKSigner alloc] initWithCredentials:credentials];
              signer.expiredTimeInSeconds = 3600;
          
              return signer;
          }
          
          NSMutableURLRequest* createRequest() {
              // create url directly, or use NSURLComponents.
              NSURL* url = [NSURL URLWithString:@"http://bj.bcebos.com/v1/bucket/object?append"];
          
              // create request.
              NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
              request.HTTPMethod = @"POST";
              [request setValue:@"<length>" forHTTPHeaderField:@"Content-Length"];
              [request setValue:@"<md5>" forHTTPHeaderField:@"Content-MD5"];
              [request setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
          
              // custom metadata key should begin with lower case prefix 'x-bce-'.
              [request setValue:@"2017-01-08T21:42:30Z" forHTTPHeaderField:@"x-bce-user-metadata-createtime"];
          
              // Host will be set when call sign.
              //[request setValue:@"bj.bcebos.com" forHTTPHeaderField:@"Host"];
          
              return request;
          }
          
          void sign() {
              id<BDCloudSigner> signer = createSigner();
              NSMutableURLRequest* request = createRequest();
              if (![signer sign:request]) {
                  return;
              }
          
              // url
              NSURL* fileURL = [NSURL fileURLWithPath:@"<file path>"];
          
              // send request
              // sample purpose, don't care task will running correctly.
              [[NSURLSession sharedSession] uploadTaskWithRequest:request
                                                         fromFile:fileURL];
          }
          
          - (void)testAKSKSigner {
              sign();
          }
          
          @end
          Previous
          Include the Authentication String into the URL
          Next
          Troubleshooting of Common Signature Verification Errors