Todos os produtos
Search
Central de documentação

Alibaba Cloud SDK:Sintaxe de requisição e método de assinatura V3

Última atualização: Aug 28, 2026

O método de assinatura V3 é o mecanismo utilizado pelo API Gateway da Alibaba Cloud para autenticar requisições HTTP ou HTTPS destinadas a operações de API da Alibaba Cloud. Assine as requisições manualmente quando não desejar invocar operações de API por meio de um kit de desenvolvimento de software (SDK) ou quando seu ambiente de execução não oferecer suporte a SDKs.

Observações de uso

  • Migração da V2 — Caso utilize atualmente o método de assinatura V2 para chamar operações de API, migre diretamente para o método de assinatura V3.

  • Cobertura de SDK — O OpenAPI Explorer disponibiliza SDKs para serviços da Alibaba Cloud, cujas operações de API já suportam o método de assinatura V3.

  • Serviços com gateways autogerenciados — Determinados serviços da Alibaba Cloud utilizam gateways próprios, com mecanismos de autenticação distintos do descrito neste tópico. Antes de enviar requisições HTTP para esses serviços, consulte a documentação específica sobre o método de assinatura correspondente.

  • Simple Log Service (SLS) — Para obter mais informações sobre o método de assinatura do SLS, consulte Request signatures.

  • Object Storage Service (OSS) — Para detalhes sobre o método de assinatura do OSS, veja Signature methods.

Sintaxe de requisição HTTP

Uma requisição completa à API da Alibaba Cloud é composta pelos seguintes elementos.

Nome

Obrigatório

Descrição

Exemplo

Protocolo

Sim

Consulte as referências de API de cada serviço da Alibaba Cloud para obter detalhes de configuração. As requisições podem ser enviadas pelos protocolos HTTP ou HTTPS. Para maior segurança, recomenda-se o uso de HTTPS. Valores válidos: https:// e http://.

https://

Endpoint

Sim

Endpoint do serviço. Verifique a documentação de endpoints de cada serviço da Alibaba Cloud para visualizar os endpoints disponíveis em diferentes regiões.

ecs.cn-shanghai.aliyuncs.com

resource_URI_parameters

Sim

URI da requisição, incluindo o caminho da API e os parâmetros presentes no caminho ou na string de consulta (query string).

ImageId=win2019_1809_x64_dtc_zh-cn_40G_alibase_20230811.vhd&RegionId=cn-shanghai

RequestHeader

Sim

Cabeçalhos comuns da requisição. Geralmente incluem informações como versão da API, Host e Authorization. Para mais detalhes, consulte a seção a seguir.

Authorization: ACS3-HMAC-SHA256 Credential=YourAccessKeyId,SignedHeaders=host;x-acs-action;x-acs-content-sha256;x-acs-date;x-acs-signature-nonce;x-acs-version,Signature=06563a9e1b43f5dfe96b81484da74bceab24a1d853912eee15083a6f0f3283c0 x-acs-action: RunInstances host: ecs.cn-shanghai.aliyuncs.com x-acs-date: 2023-10-26T10:22:32Z x-acs-version: 2014-05-26 x-acs-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 x-acs-signature-nonce: 3156853299f313e23d1673dc12e1703d

RequestBody

Sim

Parâmetros de negócio definidos no corpo da requisição. Obtenha-os em OpenAPI metadata.

HTTPMethod

Sim

Método da requisição. Disponível em OpenAPI metadata.

POST

RequestHeader

Ao invocar uma operação de API da Alibaba Cloud, os cabeçalhos comuns devem conter as informações abaixo.

NomeTipoObrigatórioDescriçãoExemplo
hostStringSimEndpoint do serviço. Para mais informações, consulte .ecs.cn-shanghai.aliyuncs.com
x-acs-actionStringSimNome da operação de API. Acesse o Alibaba Cloud OpenAPI Developer Portal e pesquise a operação desejada.RunInstances
x-acs-content-sha256StringSimResultado do hash do corpo da requisição, codificado em Base16. Corresponde ao valor de HashedRequestPayload.e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-acs-dateStringSimHorário UTC no padrão ISO 8601, no formato yyyy-MM-ddTHH:mm:ssZ (exemplo: 2018-01-01T12:00:00Z). O valor deve estar dentro de 15 minutos anteriores ao envio da requisição.2023-10-26T10:22:32Z
x-acs-signature-nonceStringSimNonce da assinatura. Evita ataques de replay na rede. Utilize um nonce diferente para cada requisição. Este mecanismo aplica-se apenas ao protocolo HTTP.3156853299f313e23d1673dc12e1703d
x-acs-versionStringSimNúmero da versão da API. Para saber como obter esse número, consulte How do I obtain the API version (x-acs-version)?.2014-05-26
AuthorizationStringObrigatório para requisições não anônimasInformação de autenticação usada para validar a requisição. Formato: Authorization: SignatureAlgorithm Credential=AccessKeyId,SignedHeaders=SignedHeaders,Signature=Signature. SignatureAlgorithm é o algoritmo de criptografia da assinatura e deve ser ACS3-HMAC-SHA256. Credential corresponde ao AccessKey ID do usuário. Visualize seu AccessKey ID no Resource Access Management (RAM) console. Para criar um par de AccessKey, veja Create an AccessKey pair. SignedHeaders lista os nomes dos cabeçalhos utilizados no cálculo da assinatura. Nota: Recomenda-se incluir todos os cabeçalhos comuns, exceto Authorization, para aumentar a segurança. Signature é a assinatura da requisição. Para detalhes sobre o valor, consulte a seção Método de assinatura.ACS3-HMAC-SHA256 Credential=YourAccessKeyId,SignedHeaders=host;x-acs-action;x-acs-content-sha256;x-acs-date;x-acs-signature-nonce;x-acs-version,Signature=06563a9e1b43f5dfe96b81484da74bceab24a1d853912eee15083a6f0f3283c0
x-acs-security-tokenStringObrigatório para autenticação STSValor de SecurityToken retornado na resposta da operação AssumeRole.

Antes de calcular a assinatura

O método de assinatura V3 assina cada requisição com um par de AccessKey (AccessKey ID e AccessKey secret). As restrições a seguir determinam o que assinar e o que enviar. Revise todas elas antes de implementar o código de assinatura.

  • Conjunto de caracteres — A codificação das requisições e respostas utiliza o conjunto de caracteres UTF-8.

  • Janela de tempo — O valor de x-acs-date deve corresponder a um horário dentro dos 15 minutos anteriores ao envio da requisição.

  • Unicidade do nonce — Defina um valor exclusivo de x-acs-signature-nonce para cada requisição. O nonce previne ataques de replay na rede. Esse mecanismo aplica-se somente ao protocolo HTTP.

  • Credenciais — Para credenciais permanentes, assine a requisição com seu par de AccessKey. Para credenciais temporárias emitidas pelo Security Token Service (STS), adicione também o cabeçalho x-acs-security-token, cujo valor corresponde ao SecurityToken retornado na resposta da operação AssumeRole.

  • Metadados da API — Antes de assinar, consulte OpenAPI metadata para obter o método de requisição, nomes e tipos dos parâmetros, além da forma correta de passá-los. Caso contrário, a assinatura provavelmente falhará.

APIs estilo RPC e ROA

O campo style nos metadados da API define o CanonicalURI da requisição e os métodos suportados.

  • CanonicalURI — Em APIs estilo RPC, o CanonicalURI é uma barra invertida (/). Em APIs estilo ROA, corresponde ao valor de path nos metadados, como /api/v1/clusters.

  • Métodos de requisição — APIs estilo RPC geralmente aceitam tanto GET quanto POST. APIs estilo ROA suportam apenas um método. Para verificar os métodos aceitos por uma API, consulte OpenAPI metadata.

  • **Outros valores de style** — RPC e ROA influenciam apenas o valor do CanonicalURI. Se style tiver um valor diferente de RPC ou ROA, verifique se o parâmetro path possui valor nas informações de resumo dos metadados da API. Se houver valor, defina o CanonicalURI como o valor de path. Se não houver, use uma barra (/). Nos metadados abaixo, referentes à operação que lista clusters ACK no Container Service for Kubernetes, o valor de path é /api/v1/clusters. Nesse caso, o CanonicalURI será /api/v1/clusters.

{
  "code": 0,
  "data": {
    "summary": "Queries the details about Container Service for Kubernetes (A...",
    "path": "/api/v1/clusters",
    "methods": [
      "get"
    ],
    "schemes": [
      "http",
      "https"
    ],
    "security": [
      {
        "AK": []
      }
    ]
  }
}

Posição dos parâmetros

No API metadata, o campo in define a posição de cada parâmetro. Essa posição determina como o parâmetro é transmitido e qual content-type a requisição exige.

Posição do parâmetro

Descrição

content-type

"in": "query"

Parâmetros de consulta, localizados após o ponto de interrogação (?) ao final da URL da requisição. Pares name=value distintos são separados por e comercial (&).

Opcional. Se especificado, defina como application/json.

"in": "formData"

Parâmetros de formulário, concatenados em uma string no formato key1=value1&key2=value2&key3=value3 e enviados no corpo da requisição. Além disso, se um parâmetro for do tipo array ou object, converta o value em pares chave-valor indexados. Por exemplo, o valor object {"key":["value1","value2"]} deve ser convertido para {"key.1":"value1","key.2":"value2"}.

Obrigatório. Defina como content-type=application/x-www-form-urlencoded.

"in": "body"

Parâmetros de corpo, transmitidos diretamente no corpo da requisição.

Obrigatório. O valor de content-type varia conforme o conteúdo da requisição. Para dados JSON, use application/json. Para conteúdo binário, como fluxo de arquivo binário, utilize application/octet-stream.

Parâmetros array e object

Quando um parâmetro possuir estrutura de dados complexa, converta seu valor em pares chave-valor indexados.

Exemplo 1: {"InstanceId":["i-bp10igfmnyttXXXXXXXX","i-bp1incuofvzxXXXXXXXX","i-bp1incuofvzxXXXXXXXX","i-bp10igfmnyttXXXXXXXX","i-bp10igfmnyttXXXXXXXX","i-bp10igfmnyttXXXXXXXX","i-bp10igfmnyttXXXXXXXX","i-bp10igfmnyttXXXXXXXX","i-bp10igfmnyttXXXXXXXX","i-bp10igfmnyttXXXXXXXX","i-bp10igfmnyttXXXXXXXX","i-bp10igfmnyttXXXXXXXX"]} deve ser convertido para:

{
    "InstanceId.1": "i-bp10igfmnyttXXXXXXXX",
    "InstanceId.10": "i-bp10igfmnyttXXXXXXXX",
    "InstanceId.11": "i-bp10igfmnyttXXXXXXXX",
    "InstanceId.12": "i-bp10igfmnyttXXXXXXXX",
    "InstanceId.2": "i-bp1incuofvzxXXXXXXXX",
    "InstanceId.3": "i-bp1incuofvzxXXXXXXXX",
    "InstanceId.4": "i-bp10igfmnyttXXXXXXXX",
    "InstanceId.5": "i-bp10igfmnyttXXXXXXXX",
    "InstanceId.6": "i-bp10igfmnyttXXXXXXXX",
    "InstanceId.7": "i-bp10igfmnyttXXXXXXXX",
    "InstanceId.8": "i-bp10igfmnyttXXXXXXXX",
    "InstanceId.9": "i-bp10igfmnyttXXXXXXXX"
}

Exemplo 2: {"ImageId":"win2019_1809_x64_dtc_zh-cn_40G_alibase_20230811.vhd","RegionId":"cn-shanghai","Tag":[{"tag1":"value1","tag2":"value2"}]} deve ser convertido para:

{
    "ImageId":"win2019_1809_x64_dtc_zh-cn_40G_alibase_20230811.vhd",
    "RegionId":"cn-shanghai",
    "Tag.1.tag1":"value1",
    "Tag.1.tag2":"value2"
}

Método de assinatura

A assinatura e autenticação das requisições ocorrem por meio de um par de AccessKey (AccessKey ID e AccessKey secret). Para cada requisição HTTP ou HTTPS, o API Gateway da Alibaba Cloud recalcula a assinatura com base nos parâmetros recebidos e compara o resultado com a assinatura enviada. Esse processo valida a identidade do solicitante e garante a integridade e segurança dos dados transmitidos.

Os quatro passos a seguir geram o valor do cabeçalho de requisição Authorization.

Passo 1: Construir a requisição canônica

O pseudocódigo abaixo ilustra a construção de uma requisição canônica (CanonicalRequest):

CanonicalRequest =
  HTTPRequestMethod + '\n' +    // The HTTP request method, in uppercase letters.
  CanonicalURI + '\n' +         // The canonical URI.
  CanonicalQueryString + '\n' + // The canonical query string.
  CanonicalHeaders + '\n' +     // The canonical headers.
  SignedHeaders + '\n' +        // The signed headers.
  HashedRequestPayload      // The hash value of the request body.

HTTPRequestMethod (método de requisição): nome do método HTTP em letras maiúsculas, como GET ou POST.

CanonicalURI (URI canônica): caminho do recurso codificado na URL. Corresponde à parte entre o host e a string de consulta, incluindo a barra (/) logo após o host, mas excluindo o ponto de interrogação (?) que antecede a query string. Utilize a URI canônica ao enviar a requisição. Codifique cada segmento da URI (cada string separada por barra - /) em UTF-8 seguindo as regras da RFC3986:

  • Os caracteres A-Z, a-z, 0-9, além de -, _, . e ~ não são codificados. Demais caracteres devem ser representados por um sinal de porcentagem (%) seguido do código ASCII hexadecimal correspondente. Por exemplo, aspas duplas (") tornam-se %22. As sequências abaixo exigem tratamento especial:

Sequência

Resultado esperado

Espaço ( )

%20

Asterisco (*)

%2A

%7E na saída codificada

Til (~)

Ao utilizar java.net.URLEncoder da biblioteca padrão Java, primeiro codifique a string chamando encode. Em seguida, substitua na string resultante os sinais de mais (+) por %20, asteriscos (*) por %2A e %7E por til (~). O resultado final estará em conformidade com as regras acima.

Para APIs estilo RPC, utilize uma barra (/) como CanonicalURI.

Em APIs estilo ROA, esse parâmetro corresponde ao valor de path nos metadados do OpenAPI, por exemplo, /api/v1/clusters.

CanonicalQueryString (string de consulta canônica): no OpenAPI metadata, caso os parâmetros da API incluam "in":"query", concatene-os seguindo estas regras:

  • Ordene os parâmetros em ordem crescente pelo nome.

  • Codifique individualmente nome e valor de cada parâmetro em UTF-8, conforme as regras da RFC3986. As regras são idênticas às de codificação da CanonicalURI descritas anteriormente.

  • Una nome e valor codificados com um sinal de igual (=). Se o parâmetro não tiver valor, utilize uma string vazia.

  • Separe múltiplos parâmetros com e comercial (&).

  • If a request parameter is of the array or object type, convert the parameter value into indexed key-value pairs.

  • Caso um parâmetro seja uma string JSON, a ordem dos campos internos não altera o cálculo da assinatura.

  • Na ausência de string de consulta, utilize uma string vazia como valor canônico. Exemplo:

ImageId=win2019_1809_x64_dtc_zh-cn_40G_alibase_20230811.vhd&RegionId=cn-shanghai

HashedRequestPayload: calcule o hash do corpo da requisição e codifique o resultado em Base16 para obter o HashedRequestPayload. Defina o cabeçalho x-acs-content-sha256 com esse valor. Para a lista de cabeçalhos comuns, consulte . Segue o pseudocódigo:

HashedRequestPayload = HexEncode(Hash(RequestBody))
  • No OpenAPI metadata, se os parâmetros da API incluírem "in": "body" ou "in": "formData", transmita-os no corpo da requisição:

    • Se nenhum parâmetro for enviado no corpo, defina-o como string vazia.

    • Se houver parâmetros com "in": "formData", concatene-os no formato key1=value1&key2=value2&key3=value3 e adicione content-type=application/x-www-form-urlencoded aos cabeçalhos. Observe que if a request parameter is of the array or object type, you must convert the parameter value into indexed key-value pairs.

    • Se houver parâmetros com "in": "body", adicione content-type aos cabeçalhos. O valor depende do tipo de conteúdo da requisição. Exemplos:

      • Para dados JSON, defina content-type como application/json.

      • Para conteúdo binário, como fluxo de arquivo, utilize application/octet-stream.

  • Hash representa a função de resumo criptográfico. Apenas o algoritmo SHA256 é suportado.

  • HexEncode é a função de codificação que retorna o resumo em notação hexadecimal minúscula (Base16). Valor de exemplo do HashedRequestPayload quando o corpo está vazio:

e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

CanonicalHeaders (cabeçalhos canônicos): concatene os cabeçalhos comuns seguindo as regras abaixo. Para a lista completa, veja .

  • Selecione os cabeçalhos com prefixo x-acs-, além de host e content-type.

  • Converta os nomes para minúsculas e ordene-os alfabeticamente.

  • Remova espaços em branco no início e fim dos valores.

  • Una nome e valor com dois pontos (:) e adicione uma quebra de linha (\n) para formar uma entrada canônica (CanonicalHeaderEntry).

  • Concatene todas as entradas canônicas em uma única string. Todos os cabeçalhos, exceto Authorization, devem participar do cálculo da assinatura caso atendam aos requisitos acima.

Pseudocódigo correspondente:

CanonicalHeaderEntry = Lowercase(HeaderName) + ':' + Trim(HeaderValue) + '\n'

CanonicalHeaders =
    CanonicalHeaderEntry0 + CanonicalHeaderEntry1 + ... + CanonicalHeaderEntryN

Exemplo:

host:ecs.cn-shanghai.aliyuncs.com
x-acs-action:RunInstances
x-acs-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-acs-date:2023-10-26T10:22:32Z
x-acs-signature-nonce:3156853299f313e23d1673dc12e1703d
x-acs-version:2014-05-26

SignedHeaders (lista de cabeçalhos assinados): especifica quais cabeçalhos comuns participam do cálculo da assinatura na requisição atual. Os nomes correspondem exatamente aos parâmetros em CanonicalHeaders. Construa a lista assim:

  • Converta os nomes dos cabeçalhos em CanonicalHeaders para minúsculas.

  • Ordene-os alfabeticamente e separe-os por ponto e vírgula (;). Pseudocódigo:

SignedHeaders = Lowercase(HeaderName0) + ';' + Lowercase(HeaderName1) + ... + Lowercase(HeaderNameN)

Exemplo:

host;x-acs-action;x-acs-content-sha256;x-acs-date;x-acs-signature-nonce;x-acs-version

Passo 2: Construir a string a ser assinada

Monte a string a ser assinada (stringToSign) conforme o pseudocódigo abaixo:

StringToSign =
    SignatureAlgorithm + '\n' +
    HashedCanonicalRequest
  • SignatureAlgorithm: Apenas o algoritmo ACS3-HMAC-SHA256 é suportado no protocolo de assinatura.

  • HashedCanonicalRequest: String de resumo da requisição canônica. O pseudocódigo a seguir demonstra o cálculo:

HashedCanonicalRequest = HexEncode(Hash(CanonicalRequest))
  • Hash representa a função de resumo criptográfico. Somente SHA256 é aceito.

  • HexEncode é a função que retorna o resumo em hexadecimal minúsculo (Base16). Exemplo:

ACS3-HMAC-SHA256
7ea06492da5221eba5297e897ce16e55f964061054b7695beedaac1145b1e259

Passo 3: Calcular a string de assinatura

Calcule a assinatura (Signature) utilizando o pseudocódigo a seguir.

Signature = HexEncode(SignatureMethod(Secret, StringToSign))
  • StringToSign: string construída no Passo 2, codificada em UTF-8.

  • SignatureMethod: Algoritmo HMAC-SHA256 utilizado para assinatura.

  • Secret: AccessKey secret.

  • HexEncode: Função de codificação que retorna o resumo em hexadecimal minúsculo (Base16). Exemplo:

06563a9e1b43f5dfe96b81484da74bceab24a1d853912eee15083a6f0f3283c0

Passo 4: Adicionar a assinatura à requisição

Após calcular a assinatura, monte o cabeçalho Authorization no seguinte formato: Authorization: <SignatureAlgorithm> Credential=<AccessKeyId>,SignedHeaders=<SignedHeaders>,Signature=<Signature>.

Exemplo:

ACS3-HMAC-SHA256 Credential=YourAccessKeyId,SignedHeaders=host;x-acs-action;x-acs-content-sha256;x-acs-date;x-acs-signature-nonce;x-acs-version,Signature=06563a9e1b43f5dfe96b81484da74bceab24a1d853912eee15083a6f0f3283c0

Valide sua implementação de assinatura

Execute os quatro passos anteriores com os valores hipotéticos abaixo e compare sua saída com o resultado esperado em cada etapa. Identifique qualquer divergência antes de enviar uma requisição real: uma incompatibilidade em qualquer passo gera uma assinatura rejeitada pelo gateway.

Parâmetro obrigatório

Valor hipotético

AccessKeyID

YourAccessKeyId

AccessKeySecret

YourAccessKeySecret

x-acs-signature-nonce

3156853299f313e23d1673dc12e1703d

x-acs-date

2023-10-26T10:22:32Z

x-acs-action

RunInstances

x-acs-version

2014-05-26

host

ecs.cn-shanghai.aliyuncs.com

Os seguintes parâmetros de requisição de API são utilizados:

Parâmetro

Valor

ImageId

win2019_1809_x64_dtc_zh-cn_40G_alibase_20230811.vhd

RegionId

cn-shanghai

Passo 1: Construir a requisição canônica

POST
/
ImageId=win2019_1809_x64_dtc_zh-cn_40G_alibase_20230811.vhd&RegionId=cn-shanghai
host:ecs.cn-shanghai.aliyuncs.com
x-acs-action:RunInstances
x-acs-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-acs-date:2023-10-26T10:22:32Z
x-acs-signature-nonce:3156853299f313e23d1673dc12e1703d
x-acs-version:2014-05-26

host;x-acs-action;x-acs-content-sha256;x-acs-date;x-acs-signature-nonce;x-acs-version
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

Passo 2: Construir a string a ser assinada

ACS3-HMAC-SHA256
7ea06492da5221eba5297e897ce16e55f964061054b7695beedaac1145b1e259

Passo 3: Calcular a string de assinatura

06563a9e1b43f5dfe96b81484da74bceab24a1d853912eee15083a6f0f3283c0

Passo 4: Adicionar a assinatura à requisição

POST /?ImageId=win2019_1809_x64_dtc_zh-cn_40G_alibase_20230811.vhd&RegionId=cn-shanghai HTTP/1.1
Authorization: ACS3-HMAC-SHA256 Credential=YourAccessKeyId,SignedHeaders=host;x-acs-action;x-acs-content-sha256;x-acs-date;x-acs-signature-nonce;x-acs-version,Signature=06563a9e1b43f5dfe96b81484da74bceab24a1d853912eee15083a6f0f3283c0
x-acs-action: RunInstances
host: ecs.cn-shanghai.aliyuncs.com
x-acs-date: 2023-10-26T10:22:32Z
x-acs-version: 2014-05-26
x-acs-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-acs-signature-nonce: 3156853299f313e23d1673dc12e1703d
user-agent: AlibabaCloud (Mac OS X; x86_64) Java/1.8.0_352-b08 tea-util/0.2.6 TeaDSL/1
accept: application/json

Os cabeçalhos user-agent e accept da requisição acima não constam em SignedHeaders e, portanto, não participam do cálculo da assinatura.

Se sua saída diferir do esperado em alguma etapa, corrija-a antes de prosseguir. Caso a requisição ainda falhe com erro de assinatura, utilize as causas e soluções da seção FAQ deste tópico para identificar a discrepância.

Exemplos de assinatura

Os exemplos a seguir apresentam implementações completas do método de assinatura descrito anteriormente nas principais linguagens de programação. Este código serve apenas para ilustrar o funcionamento do método e não se aplica universalmente. Cada aba indica a versão do runtime em que o exemplo foi validado, bem como as dependências necessárias.

A Alibaba Cloud também oferece SDKs para diversas linguagens de programação e frameworks de desenvolvimento. Esses SDKs gerenciam o processo de assinatura automaticamente e aceleram a criação de aplicações na Alibaba Cloud. (Recomendado) Utilize um SDK em vez de assinar as requisições manualmente.

Instruções

  1. Leia o conteúdo em inglês para compreender O QUE precisa ser comunicado

  2. Escreva o texto em português do Brasil DO ZERO — esqueça a estrutura das frases em inglês

  3. Mantenha toda a formatação markdown, blocos de código, links e imagens exatamente como estão

  4. Copie os placeholders de xref ({XREF_N}) literalmente, sem traduzi-los ou modificá-los

  5. Aplique rigorosamente todas as regras específicas do idioma

  6. Siga as regras de stopwords com tolerância zero

  7. Use o modo imperativo em passos numerados e listas procedimentais

  8. Garanta a consistência terminológica — o mesmo termo deve ter sempre a mesma tradução

  9. Varie os inícios de frase em listas e tabelas — nenhum iniciador pode se repetir 3 vezes ou mais

  10. Retorne APENAS o documento markdown em português do Brasil, sem explicações

    Java

    O código de exemplo foi testado no JDK 1.8. Ajuste o código conforme necessário para o seu ambiente.

    Para executar o exemplo em Java, adicione as seguintes dependências do Maven ao arquivo pom.xml.

    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.13</version>
    </dependency>
    <dependency>
         <groupId>com.google.code.gson</groupId>
         <artifactId>gson</artifactId>
         <version>2.9.0</version>
     </dependency>
    import com.google.gson.Gson;
    import com.google.gson.GsonBuilder;
    import org.apache.http.client.methods.*;
    import org.apache.http.client.utils.URIBuilder;
    import org.apache.http.entity.ByteArrayEntity;
    import org.apache.http.entity.ContentType;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import javax.xml.bind.DatatypeConverter;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.net.URISyntaxException;
    import java.net.URLEncoder;
    import java.nio.charset.StandardCharsets;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.security.MessageDigest;
    import java.text.SimpleDateFormat;
    import java.util.*;
    import java.util.stream.Collectors;
    
    public class SignatureDemo {
    
        public static class SignatureRequest {
            // HTTP Method
            private final String httpMethod;
            // The request path.
            private final String canonicalUri;
            // endpoint
            private final String host;
            // API name
            private final String xAcsAction;
            // API version
            private final String xAcsVersion;
            // headers
            private final Map<String, String> headers = new TreeMap<>();
            // The body parameters.
            private byte[] body;
            // The query parameters.
            private final Map<String, Object> queryParam = new TreeMap<>();
    
            public SignatureRequest(String httpMethod, String canonicalUri, String host,
                                    String xAcsAction, String xAcsVersion) {
                this.httpMethod = httpMethod;
                this.canonicalUri = canonicalUri;
                this.host = host;
                this.xAcsAction = xAcsAction;
                this.xAcsVersion = xAcsVersion;
                initHeader();
            }
    
            private void initHeader() {
                headers.put("host", host);
                headers.put("x-acs-action", xAcsAction);
                headers.put("x-acs-version", xAcsVersion);
    
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
                sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
                headers.put("x-acs-date", sdf.format(new Date()));
                headers.put("x-acs-signature-nonce", UUID.randomUUID().toString());
            }
    
            public String getHttpMethod() {
                return httpMethod;
            }
    
            public String getCanonicalUri() {
                return canonicalUri;
            }
    
            public String getHost() {
                return host;
            }
    
            public Map<String, String> getHeaders() {
                return headers;
            }
    
            public byte[] getBody() {
                return body;
            }
    
            public Map<String, Object> getQueryParam() {
                return queryParam;
            }
    
            public void setBody(byte[] body) {
                this.body = body;
            }
    
            public void setQueryParam(String key, Object value) {
                this.queryParam.put(key, value);
            }
    
            public void setHeaders(String key, String value) {
                this.headers.put(key, value);
            }
        }
    
        public static class SignatureService {
            private static final String ALGORITHM = "ACS3-HMAC-SHA256";
    
            /**
             * Calculate the signature.
             */
            public static void getAuthorization(SignatureRequest signatureRequest,
                                                String accessKeyId, String accessKeySecret, String securityToken) {
                try {
                    // Process complex query parameters.
                    Map<String, Object> processedQueryParams = new TreeMap<>();
                    processObject(processedQueryParams, "", signatureRequest.getQueryParam());
                    signatureRequest.getQueryParam().clear();
                    signatureRequest.getQueryParam().putAll(processedQueryParams);
    
                    // Step 1: Construct the canonical request string.
                    String canonicalQueryString = buildCanonicalQueryString(signatureRequest.getQueryParam());
    
                    // Calculate the hash value of the request body.
                    String hashedRequestPayload = calculatePayloadHash(signatureRequest.getBody());
                    signatureRequest.setHeaders("x-acs-content-sha256", hashedRequestPayload);
    
                    // Add the security token if it exists.
                    if (securityToken != null && !securityToken.isEmpty()) {
                        signatureRequest.setHeaders("x-acs-security-token", securityToken);
                    }
    
                    // Construct the canonical headers and the signed headers.
                    CanonicalHeadersResult canonicalHeadersResult = buildCanonicalHeaders(signatureRequest.getHeaders());
    
                    // Construct the canonical request.
                    String canonicalRequest = String.join("\n",
                            signatureRequest.getHttpMethod(),
                            signatureRequest.getCanonicalUri(),
                            canonicalQueryString,
                            canonicalHeadersResult.canonicalHeaders,
                            canonicalHeadersResult.signedHeaders,
                            hashedRequestPayload);
    
                    System.out.println("canonicalRequest=========>\n" + canonicalRequest);
    
                    // Step 2: Construct the string-to-sign.
                    String hashedCanonicalRequest = sha256Hex(canonicalRequest.getBytes(StandardCharsets.UTF_8));
                    String stringToSign = ALGORITHM + "\n" + hashedCanonicalRequest;
                    System.out.println("stringToSign=========>\n" + stringToSign);
    
                    // Step 3: Calculate the signature.
                    String signature = DatatypeConverter.printHexBinary(
                                    hmac256(accessKeySecret.getBytes(StandardCharsets.UTF_8), stringToSign))
                            .toLowerCase();
                    System.out.println("signature=========>" + signature);
    
                    // Step 4: Construct the Authorization header.
                    String authorization = String.format("%s Credential=%s,SignedHeaders=%s,Signature=%s",
                            ALGORITHM, accessKeyId, canonicalHeadersResult.signedHeaders, signature);
    
                    System.out.println("authorization=========>" + authorization);
                    signatureRequest.getHeaders().put("Authorization", authorization);
                } catch (Exception e) {
                    throw new RuntimeException("Failed to generate authorization", e);
                }
            }
    
            /**
             * Process request parameters of the formData type.
             */
            private static String formDataToString(Map<String, Object> formData) {
                Map<String, Object> tileMap = new HashMap<>();
                processObject(tileMap, "", formData);
                StringBuilder result = new StringBuilder();
                boolean first = true;
                String symbol = "&";
                for (Map.Entry<String, Object> entry : tileMap.entrySet()) {
                    String value = String.valueOf(entry.getValue());
                    if (value != null && !value.isEmpty()) {
                        if (first) {
                            first = false;
                        } else {
                            result.append(symbol);
                        }
                        result.append(percentCode(entry.getKey()));
                        result.append("=");
                        result.append(percentCode(value));
                    }
                }
    
                return result.toString();
            }
    
            /**
             * Construct the canonical query string.
             */
            private static String buildCanonicalQueryString(Map<String, Object> queryParams) {
                return queryParams.entrySet().stream()
                        .map(entry -> percentCode(entry.getKey()) + "=" +
                                percentCode(String.valueOf(entry.getValue())))
                        .collect(Collectors.joining("&"));
            }
    
            /**
             * Calculate the hash value of the request body.
             */
            private static String calculatePayloadHash(byte[] body) throws Exception {
                if (body != null) {
                    return sha256Hex(body);
                } else {
                    return sha256Hex("".getBytes(StandardCharsets.UTF_8));
                }
            }
    
            /**
             * Construct the canonical headers.
             */
            private static CanonicalHeadersResult buildCanonicalHeaders(Map<String, String> headers) {
                List<Map.Entry<String, String>> signedHeaders = headers.entrySet().stream()
                        .filter(entry -> {
                            String key = entry.getKey().toLowerCase();
                            return key.startsWith("x-acs-") || "host".equals(key) || "content-type".equals(key);
                        })
                        .sorted(Map.Entry.comparingByKey())
                        .collect(Collectors.toList());
    
                StringBuilder canonicalHeaders = new StringBuilder();
                StringBuilder signedHeadersString = new StringBuilder();
    
                for (Map.Entry<String, String> entry : signedHeaders) {
                    String lowerKey = entry.getKey().toLowerCase();
                    String value = entry.getValue().trim();
                    canonicalHeaders.append(lowerKey).append(":").append(value).append("\n");
                    signedHeadersString.append(lowerKey).append(";");
                }
    
                if (signedHeadersString.length() > 0) {
                    signedHeadersString.setLength(signedHeadersString.length() - 1); // Remove the trailing semicolon.
                }
    
                return new CanonicalHeadersResult(canonicalHeaders.toString(), signedHeadersString.toString());
            }
    
            private static class CanonicalHeadersResult {
                final String canonicalHeaders;
                final String signedHeaders;
    
                CanonicalHeadersResult(String canonicalHeaders, String signedHeaders) {
                    this.canonicalHeaders = canonicalHeaders;
                    this.signedHeaders = signedHeaders;
                }
            }
    
            /**
             * Process complex object parameters.
             */
            private static void processObject(Map<String, Object> map, String key, Object value) {
                if (value == null) {
                    return;
                }
    
                if (key == null) {
                    key = "";
                }
    
                if (value instanceof List<?>) {
                    List<?> list = (List<?>) value;
                    for (int i = 0; i < list.size(); ++i) {
                        processObject(map, key + "." + (i + 1), list.get(i));
                    }
                } else if (value instanceof Map<?, ?>) {
                    Map<?, ?> subMap = (Map<?, ?>) value;
                    for (Map.Entry<?, ?> entry : subMap.entrySet()) {
                        processObject(map, key + "." + entry.getKey().toString(), entry.getValue());
                    }
                } else {
                    if (key.startsWith(".")) {
                        key = key.substring(1);
                    }
    
                    if (value instanceof byte[]) {
                        map.put(key, new String((byte[]) value, StandardCharsets.UTF_8));
                    } else {
                        map.put(key, String.valueOf(value));
                    }
                }
            }
    
            /**
             * Perform the HMAC-SHA256 calculation.
             */
            private static byte[] hmac256(byte[] secretKey, String str) throws Exception {
                Mac mac = Mac.getInstance("HmacSHA256");
                SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, mac.getAlgorithm());
                mac.init(secretKeySpec);
                return mac.doFinal(str.getBytes(StandardCharsets.UTF_8));
            }
    
            /**
             * Perform the SHA-256 hash calculation.
             */
            private static String sha256Hex(byte[] input) throws Exception {
                MessageDigest md = MessageDigest.getInstance("SHA-256");
                byte[] digest = md.digest(input);
                return DatatypeConverter.printHexBinary(digest).toLowerCase();
            }
    
            /**
             * Perform URL encoding.
             */
            public static String percentCode(String str) {
                if (str == null) {
                    return "";
                }
                try {
                    return URLEncoder.encode(str, "UTF-8")
                            .replace("+", "%20")
                            .replace("*", "%2A")
                            .replace("%7E", "~");
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException("UTF-8 encoding not supported", e);
                }
            }
        }
    
        /**
         * This is a signature example. Replace the sample parameters in the main method with your actual values.
         * ROA-style and RPC-style APIs differ only in how the canonicalUri value is obtained. The rest of the process is the same.
         * <p>
         * Obtain the request method (methods), request parameter name (name), request parameter type (type), and request parameter position (in) from the API metadata, and then encapsulate the parameters into SignatureRequest.
         * 1. If a request parameter is marked "in":"query" in the metadata, pass it in queryParam. Note: For an RPC-style API, this type of parameter can also be passed in the body, with content-type set to application/x-www-form-urlencoded. See example 3.
         * 2. If a request parameter is marked "in": "body" in the metadata, pass it in the body, with the MIME type set to application/octet-stream or application/json. Note: For an RPC-style API, application/json is not recommended. Use example 3 instead.
         * 3. If a request parameter is marked "in": "formData" in the metadata, pass it in the body, with the MIME type set to application/x-www-form-urlencoded.
         */
        public static void main(String[] args) throws IOException {
            // Obtain the AccessKey pair from environment variables.
            String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
            String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
            String securityToken = System.getenv("ALIBABA_CLOUD_SECURITY_TOKEN");
    
            if (accessKeyId == null || accessKeySecret == null) {
                System.err.println("Set the ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables.");
                return;
            }
    
            // RPC-style API example 1: the request parameter is "in":"query". This example uses the DescribeInstanceStatus operation of ECS.
            SignatureRequest signatureRequest = new SignatureRequest(
                    "POST",
                    "/",
                    "ecs.cn-hangzhou.aliyuncs.com",
                    "DescribeInstanceStatus",
                    "2014-05-26"
            );
            signatureRequest.setQueryParam("RegionId", "cn-hangzhou");
            signatureRequest.setQueryParam("InstanceId", Arrays.asList("i-bp10igfmnyttXXXXXXXX", "i-bp1incuofvzxXXXXXXXX"));
    
            /*// RPC-style API example 2: the request parameter is "in":"body" (file upload scenario). This example uses the RecognizeGeneral operation of OCR.
            SignatureRequest signatureRequest = new SignatureRequest(
                    "POST",
                    "/",
                    "ocr-api.cn-hangzhou.aliyuncs.com",
                    "RecognizeGeneral",
                    "2021-07-07");
            signatureRequest.setBody(Files.readAllBytes(Paths.get("D:\\test.jpeg")));
            signatureRequest.setHeaders("content-type", "application/octet-stream");*/
    
            /*// RPC-style API example 3: the request parameter is "in": "formData" or "in":"body" (non-file upload scenario). This example uses the TranslateGeneral operation of Machine Translation.
            String httpMethod = "POST";
            String canonicalUri = "/";
            String host = "mt.aliyuncs.com";
            String xAcsAction = "TranslateGeneral";
            String xAcsVersion = "2018-10-12";
            SignatureRequest signatureRequest = new SignatureRequest(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion);
            Map<String, Object> body = new HashMap<>();
            body.put("FormatType", "text");
            body.put("SourceLanguage", "zh");
            body.put("TargetLanguage", "en");
            body.put("SourceText", "Hello");
            body.put("Scene", "general");
            String formDataToString = SignatureService.formDataToString(body);
            signatureRequest.setBody(formDataToString.getBytes(StandardCharsets.UTF_8));
            signatureRequest.setHeaders("content-type", "application/x-www-form-urlencoded");*/
    
            /*// ROA-style API POST request example. This example creates a cluster in Container Service for Kubernetes (ACK).
            SignatureRequest signatureRequest = new SignatureRequest(
                    "POST",
                    "/clusters",
                    "cs.cn-chengdu.aliyuncs.com",
                    "CreateCluster",
                    "2015-12-15");
            TreeMap<String, Object> body = new TreeMap<>();
            body.put("name", "Test");
            body.put("cluster_type", "ManagedKubernetes");
            body.put("kubernetes_version", "1.34.1-aliyun.1");
            body.put("region_id", "cn-chengdu");
            body.put("snat_entry", true);
            body.put("deletion_protection", true);
            body.put("proxy_mode", "ipvs");
            body.put("profile", "Default");
            body.put("timezone", "Asia/Shanghai");
            body.put("cluster_spec", "ack.pro.small");
            body.put("enable_rrsa", false);
            body.put("service_cidr", "192.168.0.0/16");
            body.put("zone_ids", Arrays.asList("cn-chengdu-b","cn-chengdu-b"));
            Gson gson = (new GsonBuilder()).disableHtmlEscaping().create();
            signatureRequest.setBody(gson.toJson(body).getBytes(StandardCharsets.UTF_8));
            signatureRequest.setHeaders("content-type", "application/json");*/
    
            /*// ROA-style API GET request. This example queries cluster information in Container Service for Kubernetes.
            SignatureRequest signatureRequest = new SignatureRequest(
                    "GET",
                    "/clusters/" + SignatureService.percentCode("c299f90b63b************") + "/resources",
                    "cs.cn-chengdu.aliyuncs.com",
                    "DescribeClusterResources",
                    "2015-12-15");
            signatureRequest.setQueryParam("with_addon_resources", true);*/
    
            /*// ROA-style API DELETE request. This example deletes a cluster.
            SignatureRequest signatureRequest = new SignatureRequest(
                    "DELETE",
                    "/clusters/" + SignatureService.percentCode("c299f90b63b************"),
                    "cs.cn-chengdu.aliyuncs.com",
                    "DeleteCluster",
                    "2015-12-15");*/
    
            // Generate the signature.
            SignatureService.getAuthorization(signatureRequest, accessKeyId, accessKeySecret, securityToken);
    
            // Test whether the API operation can be called successfully.
            callApi(signatureRequest);
        }
    
        /**
         * For testing only.
         */
        private static void callApi(SignatureRequest signatureRequest) {
            try {
                String url = "https://" + signatureRequest.getHost() + signatureRequest.getCanonicalUri();
                URIBuilder uriBuilder = new URIBuilder(url);
    
                // Add the query parameters.
                for (Map.Entry<String, Object> entry : signatureRequest.getQueryParam().entrySet()) {
                    uriBuilder.addParameter(entry.getKey(), String.valueOf(entry.getValue()));
                }
                HttpUriRequest httpRequest;
                switch (signatureRequest.getHttpMethod()) {
                    case "GET":
                        httpRequest = new HttpGet(uriBuilder.build());
                        break;
                    case "POST":
                        HttpPost httpPost = new HttpPost(uriBuilder.build());
                        if (signatureRequest.getBody() != null) {
                            httpPost.setEntity(new ByteArrayEntity(signatureRequest.getBody(), ContentType.create(signatureRequest.getHeaders().get("content-type"))));
                        }
                        httpRequest = httpPost;
                        break;
                    case "DELETE":
                        httpRequest = new HttpDelete(uriBuilder.build());
                        break;
                    default:
                        System.out.println("Unsupported HTTP method: " + signatureRequest.getHttpMethod());
                        throw new IllegalArgumentException("Unsupported HTTP method");
                }
    
                // Add the request headers.
                for (Map.Entry<String, String> entry : signatureRequest.getHeaders().entrySet()) {
                    httpRequest.addHeader(entry.getKey(), entry.getValue());
                }
    
                // Send the request.
                try (CloseableHttpClient httpClient = HttpClients.createDefault();
                     CloseableHttpResponse response = httpClient.execute(httpRequest)) {
                    String result = EntityUtils.toString(response.getEntity(), "UTF-8");
                    System.out.println("API Response: " + result);
                }
            } catch (IOException | URISyntaxException e) {
                throw new RuntimeException("Failed to call API", e);
            }
        }
    }

    Python

    O código de exemplo foi testado no Python 3.12.3. Ajuste o código conforme necessário para o seu ambiente.

    Instale manualmente as bibliotecas pytz e requests. Execute os comandos abaixo no terminal, de acordo com a versão do Python que você utiliza.

    Python3

    pip3 install pytz
    pip3 install requests
    import hashlib
    import hmac
    import json
    import os
    import uuid
    from collections import OrderedDict
    from datetime import datetime
    from typing import Any, Dict, List, Optional, Union
    from urllib.parse import quote_plus, urlencode
    
    import pytz
    import requests
    
    class SignatureRequest:
        def __init__(
                self,
                http_method: str,
                canonical_uri: str,
                host: str,
                x_acs_action: str,
                x_acs_version: str
        ):
            self.http_method = http_method
            self.canonical_uri = canonical_uri
            self.host = host
            self.x_acs_action = x_acs_action
            self.x_acs_version = x_acs_version
            self.headers = self._init_headers()
            self.query_param = OrderedDict()  # type: Dict[str, Any]
            self.body = None  # type: Optional[bytes]
    
        def _init_headers(self) -> Dict[str, str]:
            current_time = datetime.now(pytz.timezone('Etc/GMT'))
            headers = OrderedDict([
                ('host', self.host),
                ('x-acs-action', self.x_acs_action),
                ('x-acs-version', self.x_acs_version),
                ('x-acs-date', current_time.strftime('%Y-%m-%dT%H:%M:%SZ')),
                ('x-acs-signature-nonce', str(uuid.uuid4())),
            ])
            return headers
    
        def sorted_query_params(self) -> None:
            """Sorts query parameters by name and returns the encoded string."""
            self.query_param = dict(sorted(self.query_param.items()))
    
        def sorted_headers(self) -> None:
            """Sorts request headers by name and returns the encoded string."""
            self.headers = dict(sorted(self.headers.items()))
    
    def get_authorization(request: SignatureRequest) -> None:
        try:
            new_query_param = OrderedDict()
            process_object(new_query_param, '', request.query_param)
            request.query_param.clear()
            request.query_param.update(new_query_param)
            request.sorted_query_params()
    
            # Step 1: Construct the canonical request.
            canonical_query_string = "&".join(
                f"{percent_code(quote_plus(k))}={percent_code(quote_plus(str(v)))}"
                for k, v in request.query_param.items()
            )
            hashed_request_payload = sha256_hex(request.body or b'')
            request.headers['x-acs-content-sha256'] = hashed_request_payload
    
            if SECURITY_TOKEN:
                signature_request.headers["x-acs-security-token"] = SECURITY_TOKEN
            request.sorted_headers()
    
            filtered_headers = OrderedDict()
            for k, v in request.headers.items():
                if k.lower().startswith("x-acs-") or k.lower() in ["host", "content-type"]:
                    filtered_headers[k.lower()] = v
    
            canonical_headers = "\n".join(f"{k}:{v}" for k, v in filtered_headers.items()) + "\n"
            signed_headers = ";".join(filtered_headers.keys())
    
            canonical_request = (
                f"{request.http_method}\n{request.canonical_uri}\n{canonical_query_string}\n"
                f"{canonical_headers}\n{signed_headers}\n{hashed_request_payload}"
            )
            print(canonical_request)
    
            # Step 2: Construct the string-to-sign.
            hashed_canonical_request = sha256_hex(canonical_request.encode("utf-8"))
            string_to_sign = f"{ALGORITHM}\n{hashed_canonical_request}"
            print(string_to_sign)
    
            # Step 3: Calculate the signature.
            signature = hmac256(ACCESS_KEY_SECRET.encode("utf-8"), string_to_sign).hex().lower()
    
            # Step 4: Construct the Authorization header.
            authorization = f'{ALGORITHM} Credential={ACCESS_KEY_ID},SignedHeaders={signed_headers},Signature={signature}'
            request.headers["Authorization"] = authorization
        except Exception as e:
            print("Failed to get authorization")
            print(e)
    
    def form_data_to_string(form_data: Dict[str, Any]) -> str:
        tile_map = OrderedDict()
        process_object(tile_map, "", form_data)
        return urlencode(tile_map)
    
    def process_object(result_map: Dict[str, str], key: str, value: Any) -> None:
        if value is None:
            return
    
        if isinstance(value, (list, tuple)):
            for i, item in enumerate(value):
                process_object(result_map, f"{key}.{i + 1}", item)
        elif isinstance(value, dict):
            for sub_key, sub_value in value.items():
                process_object(result_map, f"{key}.{sub_key}", sub_value)
        else:
            key = key.lstrip(".")
            result_map[key] = value.decode("utf-8") if isinstance(value, bytes) else str(value)
    
    def hmac256(key: bytes, msg: str) -> bytes:
        return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()
    
    def sha256_hex(s: bytes) -> str:
        return hashlib.sha256(s).hexdigest()
    
    def call_api(request: SignatureRequest) -> None:
        url = f"https://{request.host}{request.canonical_uri}"
        if request.query_param:
            url += "?" + urlencode(request.query_param, doseq=True, safe="*")
    
        headers = dict(request.headers)
        data = request.body
    
        try:
            response = requests.request(
                method=request.http_method, url=url, headers=headers, data=data
            )
            response.raise_for_status()
            print(response.text)
        except requests.RequestException as e:
            print("Failed to send request")
            print(e)
    
    def percent_code(encoded_str: str) -> str:
        return encoded_str.replace("+", "%20").replace("*", "%2A").replace("%7E", "~")
    
    # Obtain the AccessKey ID and AccessKey secret from environment variables.
    ACCESS_KEY_ID = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_ID")
    ACCESS_KEY_SECRET = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
    SECURITY_TOKEN = os.environ.get("ALIBABA_CLOUD_SECURITY_TOKEN")
    
    ALGORITHM = "ACS3-HMAC-SHA256"
    
    """
    This is a signature example. When you test the example, select a scenario in the main function and modify the sample values. For example, to call SendSms, select example 1 and then modify http_method, host, x_acs_action, x_acs_version, and query_param.
    ROA-style and RPC-style APIs differ only in how the canonicalUri value is obtained.
    
    Obtain the request method (methods), request parameter name (name), request parameter type (type), and request parameter position (in) from the OpenAPI metadata, and then encapsulate the parameters into SignatureRequest.
    1. If a request parameter is marked "in":"query" in the metadata, pass it in queryParam. You do not need to set content-type. Note: For an RPC-style API, this type of parameter can also be passed in the body, with content-type set to application/x-www-form-urlencoded. See example 3.
    2. If a request parameter is marked "in": "body" in the metadata, pass it in the body and set content-type based on your scenario. Note: For an RPC-style API, application/json is not recommended. Use example 3 instead.
    3. If a request parameter is marked "in": "formData" in the metadata, pass it in the body, with content-type set to application/x-www-form-urlencoded.
    """
    if __name__ == "__main__":
        # RPC-style API request example 1: the request parameter is "in":"query".
        http_method = "POST"  # The request method. You can obtain it from the metadata. POST is recommended.
        canonical_uri = "/"  # An RPC-style API has no resource path. Therefore, a forward slash (/) is used as the CanonicalURI.
        host = "ecs.cn-hangzhou.aliyuncs.com"  # The service endpoint of the Alibaba Cloud service.
        x_acs_action = "DescribeInstanceStatus"  # The API name.
        x_acs_version = "2014-05-26"  # The API version.
        signature_request = SignatureRequest(http_method, canonical_uri, host, x_acs_action, x_acs_version)
        # The request parameters of DescribeInstanceStatus are as follows:
        # RegionId is displayed as a String in the metadata, "in":"query", and is required.
        signature_request.query_param['RegionId'] = 'cn-hangzhou'
        # InstanceId is displayed as an array in the metadata, "in":"query", and is optional.
        signature_request.query_param['InstanceId'] = ["i-bp10igfmnyttXXXXXXXX", "i-bp1incuofvzxXXXXXXXX",
                                                       "i-bp1incuofvzxXXXXXXXX"]
    
        # # RPC-style API request example 2: the request parameter is "in":"body" (file upload scenario).
        # http_method = "POST"
        # canonical_uri = "/"
        # host = "ocr-api.cn-hangzhou.aliyuncs.com"
        # x_acs_action = "RecognizeGeneral"
        # x_acs_version = "2021-07-07"
        # signature_request = SignatureRequest(http_method, canonical_uri, host, x_acs_action, x_acs_version)
        # # The request parameter is marked "in": "body" in the metadata. Pass it in the body.
        # file_path = "D:\\test.png"
        # with open(file_path, 'rb') as file:
        #     # Read the image content into a byte array.
        #     signature_request.body = file.read()
        #     signature_request.headers["content-type"] = "application/octet-stream"
    
        # # RPC-style API request example 3: the request parameter is "in": "formData" or "in":"body" (non-file upload scenario).
        # http_method = "POST"
        # canonical_uri = "/"
        # host = "mt.aliyuncs.com"
        # x_acs_action = "TranslateGeneral"
        # x_acs_version = "2018-10-12"
        # signature_request = SignatureRequest(http_method, canonical_uri, host, x_acs_action, x_acs_version)
        # # The request parameters of TranslateGeneral are as follows:
        # # Context is displayed as a String in the metadata, "in":"query", and is optional.
        # signature_request.query_param['Context'] = 'Morning'
        # # Parameters such as FormatType, SourceLanguage, and TargetLanguage are marked "in":"formData" in the metadata.
        # form_data = OrderedDict()
        # form_data["FormatType"] = "text"
        # form_data["SourceLanguage"] = "zh"
        # form_data["TargetLanguage"] = "en"
        # form_data["SourceText"] = "Hello"
        # form_data["Scene"] = "general"
        # signature_request.body = bytes(form_data_to_string(form_data), 'utf-8')
        # signature_request.headers["content-type"] = "application/x-www-form-urlencoded"
    
        # # Example 4: an ROA-style API POST request.
        # http_method = "POST"
        # canonical_uri = "/clusters"
        # host = "cs.cn-beijing.aliyuncs.com"
        # x_acs_action = "CreateCluster"
        # x_acs_version = "2015-12-15"
        # signature_request = SignatureRequest(http_method, canonical_uri, host, x_acs_action, x_acs_version)
        # The request parameter is marked "in":"body" in the metadata. Pass it in the body.
        # body = OrderedDict()
        # body["name"] = "testDemo"
        # body["region_id"] = "cn-beijing"
        # body["cluster_type"] = "ExternalKubernetes"
        # body["vpcid"] = "vpc-2zeou1uod4ylaXXXXXXXX"
        # body["container_cidr"] = "172.16.1.0/20"
        # body["service_cidr"] = "10.2.0.0/24"
        # body["security_group_id"] = "sg-2ze1a0rlgeo7XXXXXXXX"
        # body["vswitch_ids"] = ["vsw-2zei30dhfldu8XXXXXXXX"]
        # signature_request.body = bytes(json.dumps(body, separators=(',', ':')), 'utf-8')
        # signature_request.headers["content-type"] = "application/json; charset=utf-8"
    
        # # Example 5: an ROA-style API GET request.
        # http_method = "GET"
        # # If canonicalUri contains a path parameter, encode the path parameter by using percent_code({path parameter}).
        # cluster_id_encode = percent_code("ca72cfced86db497cab79aa28XXXXXXXX")
        # canonical_uri = f"/clusters/{cluster_id_encode}/resources"
        # host = "cs.cn-beijing.aliyuncs.com"
        # x_acs_action = "DescribeClusterResources"
        # x_acs_version = "2015-12-15"
        # signature_request = SignatureRequest(http_method, canonical_uri, host, x_acs_action, x_acs_version)
        # signature_request.query_param['with_addon_resources'] = True
    
        # # Example 6: an ROA-style API DELETE request.
        # http_method = "DELETE"
        # # If canonicalUri contains a path parameter, encode the path parameter by using percent_code({path parameter}).
        # cluster_id_encode = percent_code("ca72cfced86db497cab79aa28XXXXXXXX")
        # canonical_uri = f"/clusters/{cluster_id_encode}"
        # host = "cs.cn-beijing.aliyuncs.com"
        # x_acs_action = "DeleteCluster"
        # x_acs_version = "2015-12-15"
        # signature_request = SignatureRequest(http_method, canonical_uri, host, x_acs_action, x_acs_version)
    
        get_authorization(signature_request)
        call_api(signature_request)

    Go

    O código de exemplo foi testado no go1.22.2. Ajuste o código conforme necessário para o seu ambiente.

    Execute o seguinte comando no terminal:

    go get github.com/google/uuid
    go get golang.org/x/exp/maps
    package main
    
    import (
        "bytes"
        "crypto/hmac"
        "crypto/sha256"
        "encoding/hex"
        "io"
        "os"
        "sort"
    
        "golang.org/x/exp/maps"
    
        "fmt"
        "net/http"
        "net/url"
        "strings"
        "time"
    
        "github.com/google/uuid"
    )
    
    type Request struct {
        httpMethod   string
        canonicalUri string
        host         string
        xAcsAction   string
        xAcsVersion  string
        headers      map[string]string
        body         []byte
        queryParam   map[string]interface{}
    }
    
    func NewRequest(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion string) *Request {
        req := &Request{
            httpMethod:   httpMethod,
            canonicalUri: canonicalUri,
            host:         host,
            xAcsAction:   xAcsAction,
            xAcsVersion:  xAcsVersion,
            headers:      make(map[string]string),
            queryParam:   make(map[string]interface{}),
        }
        req.headers["host"] = host
        req.headers["x-acs-action"] = xAcsAction
        req.headers["x-acs-version"] = xAcsVersion
        req.headers["x-acs-date"] = time.Now().UTC().Format(time.RFC3339)
        req.headers["x-acs-signature-nonce"] = uuid.New().String()
        return req
    }
    
    // os.Getenv() obtains the AccessKey ID and AccessKey secret from environment variables.
    var (
        AccessKeyId     = os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
        AccessKeySecret = os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
        SecurityToken   = os.Getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
        ALGORITHM       = "ACS3-HMAC-SHA256"
    )
    
    // This is a signature example. Replace the sample parameters in the main method with your actual values.
    // ROA-style and RPC-style APIs differ only in how the canonicalUri value is obtained. The rest of the process is the same.
    // Obtain the request method (methods), request parameter name (name), request parameter type (type), and request parameter position (in) from the API metadata, and then encapsulate the parameters into SignatureRequest.
    // 1. If a request parameter is marked "in":"query" in the metadata, pass it in queryParam. Note: For an RPC-style API, this type of parameter can also be passed in the body, with content-type set to application/x-www-form-urlencoded. See example 3.
    // 2. If a request parameter is marked "in": "body" in the metadata, pass it in the body, with the MIME type set to application/octet-stream or application/json. For an RPC-style API, application/json is not recommended. Use example 3 instead.
    // 3. If a request parameter is marked "in": "formData" in the metadata, pass it in the body, with the MIME type set to application/x-www-form-urlencoded.
    func main() {
        // RPC-style API request example 1: the request parameter is "in":"query".
        httpMethod := "POST"                   // The request method. Most RPC-style APIs support both POST and GET. POST is used in this example.
        canonicalUri := "/"                    // An RPC-style API has no resource path. Therefore, a forward slash (/) is used as the CanonicalURI.
        host := "ecs.cn-hangzhou.aliyuncs.com" // The service endpoint of the Alibaba Cloud service.
        xAcsAction := "DescribeInstanceStatus" // The API name.
        xAcsVersion := "2014-05-26"            // The API version.
        req := NewRequest(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion)
        // The request parameters of DescribeInstanceStatus are as follows:
        // RegionId is displayed as a String in the metadata, "in":"query", and is required.
        req.queryParam["RegionId"] = "cn-hangzhou"
        // InstanceId is displayed as an array in the metadata, "in":"query", and is optional.
        instanceIds := []interface{}{"i-bp10igfmnyttXXXXXXXX", "i-bp1incuofvzxXXXXXXXX", "i-bp1incuofvzxXXXXXXXX"}
        req.queryParam["InstanceId"] = instanceIds
    
        // // RPC-style API request example 2: the request parameter is "in":"body" (file upload scenario).
        // httpMethod := "POST"
        // canonicalUri := "/"
        // host := "ocr-api.cn-hangzhou.aliyuncs.com"
        // xAcsAction := "RecognizeGeneral"
        // xAcsVersion := "2021-07-07"
        // req := NewRequest(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion)
        // // Read the file content.
        // filePath := "D:\\test.png"
        // bytes, err := os.ReadFile(filePath)
        // if err != nil {
        //     fmt.Println("Error reading file:", err)
        //     return
        // }
        // req.body = bytes
        // req.headers["content-type"] = "application/octet-stream"
    
        // // RPC-style API request example 3: the request parameter is "in": "formData" or "in":"body" (non-file upload scenario).
        // httpMethod := "POST"
        // canonicalUri := "/"
        // host := "mt.aliyuncs.com"
        // xAcsAction := "TranslateGeneral"
        // xAcsVersion := "2018-10-12"
        // req := NewRequest(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion)
        // // The request parameters of TranslateGeneral are as follows:
        // // Context is displayed as a String in the metadata, "in":"query", and is optional.
        // req.queryParam["Context"] = "Morning"
        // // Parameters such as FormatType, SourceLanguage, and TargetLanguage are marked "in":"formData" in the metadata.
        // body := make(map[string]interface{})
        // body["FormatType"] = "text"
        // body["SourceLanguage"] = "zh"
        // body["TargetLanguage"] = "en"
        // body["SourceText"] = "Hello"
        // body["Scene"] = "general"
        // str := formDataToString(body)
        // req.body = []byte(*str)
        // req.headers["content-type"] = "application/x-www-form-urlencoded"
    
        // // An ROA-style API POST request.
        // httpMethod := "POST"
        // canonicalUri := "/clusters"
        // host := "cs.cn-beijing.aliyuncs.com"
        // xAcsAction := "CreateCluster"
        // xAcsVersion := "2015-12-15"
        // req := NewRequest(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion)
        // // Encapsulate the request parameters. A request parameter that is marked "in": "body" in the metadata is passed in the body.
        // body := make(map[string]interface{})
        // body["name"] = "testDemo"
        // body["region_id"] = "cn-beijing"
        // body["cluster_type"] = "ExternalKubernetes"
        // body["vpcid"] = "vpc-2zeou1uod4ylaXXXXXXXX"
        // body["container_cidr"] = "10.0.0.0/8"
        // body["service_cidr"] = "172.16.1.0/20"
        // body["security_group_id"] = "sg-2ze1a0rlgeo7XXXXXXXX"
        // vswitch_ids := []interface{}{"vsw-2zei30dhfldu8XXXXXXXX"}
        // body["vswitch_ids"] = vswitch_ids
        // jsonBytes, err := json.Marshal(body)
        // if err != nil {
        //     fmt.Println("Error marshaling to JSON:", err)
        //     return
        // }
        // req.body = []byte(jsonBytes)
        // req.headers["content-type"] = "application/json; charset=utf-8"
    
        // // An ROA-style API GET request.
        // httpMethod := "GET"
        // // If canonicalUri contains a path parameter, encode the path parameter by using percentCode({path parameter}).
        // canonicalUri := "/clusters/" + percentCode("c558c166928f9446dae400d106e124f66") + "/resources"
        // host := "cs.cn-beijing.aliyuncs.com"
        // xAcsAction := "DescribeClusterResources"
        // xAcsVersion := "2015-12-15"
        // req := NewRequest(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion)
        // req.queryParam["with_addon_resources"] = "true"
    
        // // An ROA-style API DELETE request.
        // httpMethod := "DELETE"
        // // If canonicalUri contains a path parameter, encode the path parameter by using percentCode({path parameter}).
        // canonicalUri := "/clusters/" + percentCode("c558c166928f9446dae400d106e124f66")
        // host := "cs.cn-beijing.aliyuncs.com"
        // xAcsAction := "DeleteCluster"
        // xAcsVersion := "2015-12-15"
        // req := NewRequest(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion)
    
        // Sign the request.
        getAuthorization(req)
        // Call the API operation.
        error := callAPI(req)
        if error != nil {
            println(error.Error())
        }
    }
    
    func callAPI(req *Request) error {
        urlStr := "https://" + req.host + req.canonicalUri
        q := url.Values{}
        keys := maps.Keys(req.queryParam)
        sort.Strings(keys)
        for _, k := range keys {
            v := req.queryParam[k]
            q.Set(k, fmt.Sprintf("%v", v))
        }
        urlStr += "?" + q.Encode()
        fmt.Println(urlStr)
    
        httpReq, err := http.NewRequest(req.httpMethod, urlStr, strings.NewReader(string(req.body)))
        if err != nil {
            return err
        }
    
        for key, value := range req.headers {
            httpReq.Header.Set(key, value)
        }
    
        client := &http.Client{}
        resp, err := client.Do(httpReq)
        if err != nil {
            return err
        }
        defer func(Body io.ReadCloser) {
            err := Body.Close()
            if err != nil {
                return
            }
        }(resp.Body)
        var respBuffer bytes.Buffer
        _, err = io.Copy(&respBuffer, resp.Body)
        if err != nil {
            return err
        }
        respBytes := respBuffer.Bytes()
        fmt.Println(string(respBytes))
        return nil
    }
    
    func getAuthorization(req *Request) {
        // Flatten the query parameters whose values are of the List or Map type in queryParam.
        newQueryParams := make(map[string]interface{})
        processObject(newQueryParams, "", req.queryParam)
        req.queryParam = newQueryParams
        // Step 1: Construct the canonical request.
        canonicalQueryString := ""
        keys := maps.Keys(req.queryParam)
        sort.Strings(keys)
        for _, k := range keys {
            v := req.queryParam[k]
            canonicalQueryString += percentCode(url.QueryEscape(k)) + "=" + percentCode(url.QueryEscape(fmt.Sprintf("%v", v))) + "&"
        }
        canonicalQueryString = strings.TrimSuffix(canonicalQueryString, "&")
        fmt.Printf("canonicalQueryString========>%s\n", canonicalQueryString)
    
        var bodyContent []byte
        if req.body == nil {
            bodyContent = []byte("")
        } else {
            bodyContent = req.body
        }
        hashedRequestPayload := sha256Hex(bodyContent)
        req.headers["x-acs-content-sha256"] = hashedRequestPayload
    
        if SecurityToken != "" {
            req.headers["x-acs-security-token"] = SecurityToken
        }
    
        canonicalHeaders := ""
        signedHeaders := ""
        HeadersKeys := maps.Keys(req.headers)
        sort.Strings(HeadersKeys)
        for _, k := range HeadersKeys {
            lowerKey := strings.ToLower(k)
            if lowerKey == "host" || strings.HasPrefix(lowerKey, "x-acs-") || lowerKey == "content-type" {
                canonicalHeaders += lowerKey + ":" + req.headers[k] + "\n"
                signedHeaders += lowerKey + ";"
            }
        }
        signedHeaders = strings.TrimSuffix(signedHeaders, ";")
    
        canonicalRequest := req.httpMethod + "\n" + req.canonicalUri + "\n" + canonicalQueryString + "\n" + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload
        fmt.Printf("canonicalRequest========>\n%s\n", canonicalRequest)
    
        // Step 2: Construct the string-to-sign.
        hashedCanonicalRequest := sha256Hex([]byte(canonicalRequest))
        stringToSign := ALGORITHM + "\n" + hashedCanonicalRequest
        fmt.Printf("stringToSign========>\n%s\n", stringToSign)
    
        // Step 3: Calculate the signature.
        byteData, err := hmac256([]byte(AccessKeySecret), stringToSign)
        if err != nil {
            fmt.Println(err)
            panic(err)
        }
        signature := strings.ToLower(hex.EncodeToString(byteData))
    
        // Step 4: Construct the Authorization header.
        authorization := ALGORITHM + " Credential=" + AccessKeyId + ",SignedHeaders=" + signedHeaders + ",Signature=" + signature
        req.headers["Authorization"] = authorization
    }
    
    func hmac256(key []byte, toSignString string) ([]byte, error) {
        // Instantiate the HMAC-SHA256 hash.
        h := hmac.New(sha256.New, key)
        // Write the string-to-sign.
        _, err := h.Write([]byte(toSignString))
        if err != nil {
            return nil, err
        }
        // Calculate the signature and return it.
        return h.Sum(nil), nil
    }
    
    func sha256Hex(byteArray []byte) string {
        // Instantiate the SHA-256 hash function.
        hash := sha256.New()
        // Write the string to the hash function.
        _, _ = hash.Write(byteArray)
        // Calculate the SHA-256 hash value and convert it into a lowercase hexadecimal string.
        hexString := hex.EncodeToString(hash.Sum(nil))
    
        return hexString
    }
    
    func percentCode(str string) string {
        // Replace specific encoded characters.
        str = strings.ReplaceAll(str, "+", "%20")
        str = strings.ReplaceAll(str, "*", "%2A")
        str = strings.ReplaceAll(str, "%7E", "~")
        return str
    }
    
    func formDataToString(formData map[string]interface{}) *string {
        tmp := make(map[string]interface{})
        processObject(tmp, "", formData)
        res := ""
        urlEncoder := url.Values{}
        for key, value := range tmp {
            v := fmt.Sprintf("%v", value)
            urlEncoder.Add(key, v)
        }
        res = urlEncoder.Encode()
        return &res
    }
    
    // processObject recursively processes objects and flattens complex objects such as Map and List into key-value pairs.
    func processObject(mapResult map[string]interface{}, key string, value interface{}) {
        if value == nil {
            return
        }
    
        switch v := value.(type) {
        case []interface{}:
            for i, item := range v {
                processObject(mapResult, fmt.Sprintf("%s.%d", key, i+1), item)
            }
        case map[string]interface{}:
            for subKey, subValue := range v {
                processObject(mapResult, fmt.Sprintf("%s.%s", key, subKey), subValue)
            }
        default:
            if strings.HasPrefix(key, ".") {
                key = key[1:]
            }
            if b, ok := v.([]byte); ok {
                mapResult[key] = string(b)
            } else {
                mapResult[key] = fmt.Sprintf("%v", v)
            }
        }
    }

    Node.js

    O código de exemplo foi executado no Node.js v20.13.1. Talvez seja necessário ajustar o código conforme seu ambiente real.

    Este exemplo utiliza Node.js.

    const crypto = require('crypto');
    const fs = require('fs');
    
    class Request {
        constructor(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion) {
            this.httpMethod = httpMethod;
            this.canonicalUri = canonicalUri || '/';
            this.host = host;
            this.xAcsAction = xAcsAction;
            this.xAcsVersion = xAcsVersion;
            this.headers = {};
            this.body = null;
            this.queryParam = {};
            this.initHeader();
        }
    
        initHeader() {
            const date = new Date();
            this.headers = {
                'host': this.host,
                'x-acs-action': this.xAcsAction,
                'x-acs-version': this.xAcsVersion,
                'x-acs-date': date.toISOString().replace(/\..+/, 'Z'),
                'x-acs-signature-nonce': crypto.randomBytes(16).toString('hex')
            }
        }
    }
    
    const ALGORITHM = 'ACS3-HMAC-SHA256';
    const accessKeyId = process.env.ALIBABA_CLOUD_ACCESS_KEY_ID;
    const accessKeySecret = process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET;
    const securityToken = process.env.ALIBABA_CLOUD_SECURITY_TOKEN;
    const encoder = new TextEncoder()
    
    if (!accessKeyId || !accessKeySecret) {
        console.error('ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables must be set.');
        process.exit(1);
    }
    
    function getAuthorization(signRequest) {
        try {
            newQueryParam = {};
            processObject(newQueryParam, "", signRequest.queryParam);
            signRequest.queryParam = newQueryParam;
            // Step 1: Construct the canonical request.
            const canonicalQueryString = Object.entries(signRequest.queryParam)
                .sort(([a], [b]) => a.localeCompare(b))
                .map(([key, value]) => `${percentCode(key)}=${percentCode(value)}`)
                .join('&');
    
            // The request body. If the request body is empty, such as in a GET request, RequestPayload is always an empty string.
            const requestPayload = signRequest.body || encoder.encode('');
            const hashedRequestPayload = sha256Hex(requestPayload);
            signRequest.headers['x-acs-content-sha256'] = hashedRequestPayload;
            if (securityToken) {
                signRequest.headers['x-acs-security-token'] = securityToken;
            }
    
            // Convert all keys to lowercase.
            signRequest.headers = Object.fromEntries(
                Object.entries(signRequest.headers).map(([key, value]) => [key.toLowerCase(), value])
            );
    
            const sortedKeys = Object.keys(signRequest.headers)
                .filter(key => key.startsWith('x-acs-') || key === 'host' || key === 'content-type')
                .sort();
            // The signed header list. The lowercase header names are sorted in ascending alphabetical order and separated with semicolons (;).
            const signedHeaders = sortedKeys.join(";")
            // Construct the request headers. Multiple canonical headers are sorted in ascending order by the character code of the lowercase header name and then concatenated.
            const canonicalHeaders = sortedKeys.map(key => `${key}:${signRequest.headers[key]}`).join('\n') + '\n';
    
            const canonicalRequest = [
                signRequest.httpMethod,
                signRequest.canonicalUri,
                canonicalQueryString,
                canonicalHeaders,
                signedHeaders,
                hashedRequestPayload
            ].join('\n');
            console.log('canonicalRequest=========>\n', canonicalRequest);
    
            // Step 2: Construct the string-to-sign.
            const hashedCanonicalRequest = sha256Hex(encoder.encode(canonicalRequest));
            const stringToSign = `${ALGORITHM}\n${hashedCanonicalRequest}`;
            console.log('stringToSign=========>', stringToSign);
    
            // Step 3: Calculate the signature.
            const signature = hmac256(accessKeySecret, stringToSign);
            console.log('signature=========>', signature);
    
            // Step 4: Construct the Authorization header.
            const authorization = `${ALGORITHM} Credential=${accessKeyId},SignedHeaders=${signedHeaders},Signature=${signature}`;
            console.log('authorization=========>', authorization);
            signRequest.headers['Authorization'] = authorization;
        } catch (error) {
            console.error('Failed to get authorization');
            console.error(error);
        }
    }
    
    async function callApi(signRequest) {
        try {
            let url = `https://${signRequest.host}${signRequest.canonicalUri}`;
            // Add the request parameters.
            if (signRequest.queryParam) {
                const query = new URLSearchParams(signRequest.queryParam);
                url += '?' + query.toString();
            }
            console.log('url=========>', url);
    
            // Configure the request options.
            let options = {
                method: signRequest.httpMethod.toUpperCase(),
                headers: signRequest.headers
            };
    
            // Process the request body.
            if (signRequest.body && ['POST', 'PUT'].includes(signRequest.httpMethod.toUpperCase())) {
                options.body = signRequest.body;
            }
            return (await fetch(url, options)).text();
        } catch (error) {
            console.error('Failed to send request:', error);
        }
    }
    
    function percentCode(str) {
        return encodeURIComponent(str)
            .replace(/\+/g, '%20')
            .replace(/\*/g, '%2A')
            .replace(/~/g, '%7E');
    }
    
    function hmac256(key, data) {
        const hmac = crypto.createHmac('sha256', key);
        hmac.update(data, 'utf8');
        return hmac.digest('hex').toLowerCase();
    }
    
    function sha256Hex(bytes) {
        const hash = crypto.createHash('sha256');
        const digest = hash.update(bytes).digest('hex');
        return digest.toLowerCase();
    }
    
    function formDataToString(formData) {
        const tmp = {};
        processObject(tmp, "", formData);
        let queryString = '';
        for (let [key, value] of Object.entries(tmp)) {
            if (queryString !== '') {
                queryString += '&';
            }
            queryString += encodeURIComponent(key) + '=' + encodeURIComponent(value);
        }
        return queryString;
    }
    
    function processObject(map, key, value) {
        // If the value is empty, no further processing is required.
        if (value === null) {
            return;
        }
        if (key === null) {
            key = "";
        }
    
        // If the value is of the Array type, iterate over each element in the array and process it recursively.
        if (Array.isArray(value)) {
            value.forEach((item, index) => {
                processObject(map, `${key}.${index + 1}`, item);
            });
        } else if (typeof value === 'object' && value !== null) {
            // If the value is of the Object type, iterate over each key-value pair in the object and process it recursively.
            Object.entries(value).forEach(([subKey, subValue]) => {
                processObject(map, `${key}.${subKey}`, subValue);
            });
        } else {
            // For a key that starts with a period (.), remove the leading period to keep the keys continuous.
            if (key.startsWith('.')) {
                key = key.slice(1);
            }
            map[key] = String(value);
        }
    }
    
    /**
     * This is a signature example. Replace the sample parameters in the main method with your actual values.
     * ROA-style and RPC-style APIs differ only in how the canonicalUri value is obtained. The rest of the process is the same.
     *
     * Obtain the request method (methods), request parameter name (name), request parameter type (type), and request parameter position (in) from the API metadata, and then encapsulate the parameters into SignatureRequest.
     * 1. If a request parameter is marked "in":"query" in the metadata, pass it in queryParam. Note: For an RPC-style API, this type of parameter can also be passed in the body, with content-type set to application/x-www-form-urlencoded. See example 3.
     * 2. If a request parameter is marked "in": "body" in the metadata, pass it in the body, with the MIME type set to application/octet-stream or application/json. For an RPC-style API, application/json is not recommended. Use example 3 instead.
     * 3. If a request parameter is marked "in": "formData" in the metadata, pass it in the body, with the MIME type set to application/x-www-form-urlencoded.
     */
    
    // RPC-style API request example 1: the request parameter is "in":"query".
    const httpMethod = 'POST'; // The request method. Most RPC-style APIs support both POST and GET. POST is used in this example.
    const canonicalUri = '/'; // An RPC-style API has no resource path. Therefore, a forward slash (/) is used as the CanonicalURI.
    const host = 'ecs.cn-hangzhou.aliyuncs.com'; // endpoint
    const xAcsAction = 'DescribeInstanceStatus'; // The API name.
    const xAcsVersion = '2014-05-26'; // The API version.
    const signRequest = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion, xAcsVersion);
    // The request parameters of DescribeInstanceStatus are as follows:
    signRequest.queryParam = {
        // RegionId is displayed as a String in the metadata, "in":"query", and is required.
        RegionId: 'cn-hangzhou',
        // InstanceId is displayed as an array in the metadata, "in":"query", and is optional.
        InstanceId: ["i-bp10igfmnyttXXXXXXXX", "i-bp1incuofvzxXXXXXXXX", "i-bp1incuofvzxXXXXXXXX"],
    }
    
    // // RPC-style API request example 2: the request parameter is "in":"body" (file upload scenario).
    // const httpMethod = 'POST';
    // const canonicalUri = '/';
    // const host = 'ocr-api.cn-hangzhou.aliyuncs.com';
    // const xAcsAction = 'RecognizeGeneral';
    // const xAcsVersion = '2021-07-07';
    // const signRequest = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion, xAcsVersion);
    // const filePath = 'D:\\test.png';
    // const bytes = fs.readFileSync(filePath);
    // // A request parameter that is marked "in": "body" in the metadata is passed in the body.
    // signRequest.body = bytes;
    // signRequest.headers['content-type'] = 'application/octet-stream';
    
    // // RPC-style API request example 3: the request parameter is "in": "formData" or "in":"body" (non-file upload scenario).
    // const httpMethod = 'POST'; // The request method. Most RPC-style APIs support both POST and GET. POST is used in this example.
    // const canonicalUri = '/'; // An RPC-style API has no resource path. Therefore, a forward slash (/) is used as the CanonicalURI.
    // const host = 'mt.aliyuncs.com'; // endpoint
    // const xAcsAction = 'TranslateGeneral'; // The API name.
    // const xAcsVersion = '2018-10-12'; // The API version.
    // const signRequest = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion, xAcsVersion);
    // // The request parameters of TranslateGeneral are as follows:
    // // Context is displayed as a String in the metadata, "in":"query", and is optional.
    // signRequest.queryParam["Context"] = "Morning";
    // // Parameters such as FormatType, SourceLanguage, and TargetLanguage are marked "in":"formData" in the metadata.
    // const formData = {
    //     SourceLanguage: "zh",
    //     TargetLanguage: "en",
    //     FormatType: "text",
    //     Scene: "general",
    //     SourceText: 'Hello'
    // }
    // const str = formDataToString(formData)
    // signRequest.body = encoder.encode(str);
    // signRequest.headers['content-type'] = 'application/x-www-form-urlencoded';
    
    // // An ROA-style API POST request.
    // const httpMethod = 'POST';
    // const canonicalUri = '/clusters';
    // const host = 'cs.cn-beijing.aliyuncs.com';
    // const xAcsAction = 'CreateCluster';
    // const xAcsVersion = '2015-12-15';
    // const signRequest = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion, xAcsVersion);
    // // A request parameter that is marked "in": "body" in the metadata is passed in the body.
    // const body = {
    //     name: 'testDemo',
    //     region_id: 'cn-beijing',
    //     cluster_type: 'ExternalKubernetes',
    //     vpcid: 'vpc-2zeou1uod4ylaf35teei9',
    //     container_cidr: '10.0.0.0/8',
    //     service_cidr: '172.16.3.0/20',
    //     security_group_id: 'sg-2ze1a0rlgeo7dj37dd1q',
    //     vswitch_ids: [
    //         'vsw-2zei30dhfldu8ytmtarro'
    //       ],
    // }
    // signRequest.body = encoder.encode(JSON.stringify(body));
    // signRequest.headers['content-type'] = 'application/json';
    
    // // An ROA-style API GET request.
    // const httpMethod = 'GET';
    // // If canonicalUri contains a path parameter, encode the path parameter by using percentCode({path parameter}).
    // const canonicalUri = '/clusters/' + percentCode("c28c2615f8bfd466b9ef9a76c61706e96") + '/resources';
    // const host = 'cs.cn-beijing.aliyuncs.com';
    // const xAcsAction = 'DescribeClusterResources';
    // const xAcsVersion = '2015-12-15';
    // const signRequest = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion, xAcsVersion);
    // signRequest.queryParam = {
    //     with_addon_resources: true,
    // }
    
    // // An ROA-style API DELETE request.
    // const httpMethod = 'DELETE';
    // // If canonicalUri contains a path parameter, encode the path parameter by using percentCode({path parameter}).
    // const canonicalUri = '/clusters/' + percentCode("c28c2615f8bfd466b9ef9a76c61706e96");
    // const host = 'cs.cn-beijing.aliyuncs.com';
    // const xAcsAction = 'DeleteCluster';
    // const xAcsVersion = '2015-12-15';
    // const signRequest = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion, xAcsVersion);
    
    getAuthorization(signRequest);
    // Call the API operation.
    callApi(signRequest).then(r => {
        console.log(r);
    }).catch(error => {
        console.error(error);
    });

    PHP

    O código de exemplo foi executado no PHP 7.4.33. Talvez seja necessário ajustar o código conforme seu ambiente real.

    <?php
    
    class SignatureDemo
    {
        // The encryption algorithm.
        private $ALGORITHM;
        // Access Key ID
        private $AccessKeyId;
        // Access Key Secret
        private $AccessKeySecret;
    
        private $SecurityToken;
    
        public function __construct()
        {
            date_default_timezone_set('UTC'); // Set the time zone to GMT.
            $this->AccessKeyId = getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'); // getenv() obtains the AccessKey ID of the RAM user from environment variables.
            $this->AccessKeySecret = getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'); // getenv() obtains the AccessKey secret of the RAM user from environment variables.
            $this->SecurityToken = getenv('ALIBABA_CLOUD_SECURITY_TOKEN');
            $this->ALGORITHM = 'ACS3-HMAC-SHA256'; // Set the encryption algorithm.
        }
    
        /**
         * This is a signature example. Replace the sample parameters in the main method with your actual values.
         * ROA-style and RPC-style APIs differ only in how the canonicalUri value is obtained. The rest of the process is the same.
         *
         * Obtain the request method (methods), request parameter name (name), request parameter type (type), and request parameter position (in) from the API metadata, and then encapsulate the parameters into SignatureRequest.
         * 1. If a request parameter is marked "in":"query" in the metadata, pass it in queryParam. Note: For an RPC-style API, this type of parameter can also be passed in the body, with content-type set to application/x-www-form-urlencoded. See example 3.
         * 2. If a request parameter is marked "in": "body" in the metadata, pass it in the body, with the MIME type set to application/octet-stream or application/json. For an RPC-style API, application/json is not recommended. Use example 3 instead.
         * 3. If a request parameter is marked "in": "formData" in the metadata, pass it in the body, with the MIME type set to application/x-www-form-urlencoded.
         */
        public function main()
        {
            // RPC-style API request example 1: the request parameter is "in":"query".
            $request = $this->createRequest('POST', '/', 'ecs.cn-hangzhou.aliyuncs.com', 'DescribeInstanceStatus', '2014-05-26');
            // The request parameters of DescribeInstanceStatus are as follows:
            $request['queryParam'] = [
                // RegionId is displayed as a String in the metadata, "in":"query", and is required.
                'RegionId' => 'cn-hangzhou',
                // InstanceId is displayed as an array in the metadata, "in":"query", and is optional.
                'InstanceId' => ["i-bp11ht4h2kdXXXXXXXX", "i-bp16maz3h3xgXXXXXXXX", "i-bp10r67hmslXXXXXXXX"]
            ];
    
            // // RPC-style API request example 2: the request parameter is "in":"body" (file upload scenario).
            // $request = $this->createRequest('POST', '/', 'ocr-api.cn-hangzhou.aliyuncs.com', 'RecognizeGeneral', '2021-07-07');
            // // The request parameter is marked "in": "body" in the metadata. Pass it in the body.
            // $filePath = 'D:\\test.png';
            // // Pass the binary file by using a file resource.
            // $fileResource = fopen($filePath, 'rb');
            // $request['body'] = stream_get_contents($fileResource);
            // $request['headers']['content-type'] = 'application/octet-stream'; // Set Content-Type to application/octet-stream.
            // // Close the file resource.
            // fclose($fileResource);
    
            // // RPC-style API request example 3: the request parameter is "in": "formData" or "in":"body" (non-file upload scenario).
            // $request = $this->createRequest('POST', '/', 'mt.aliyuncs.com', 'TranslateGeneral', '2018-10-12');
            // // The request parameters of TranslateGeneral are as follows:
            // $request['queryParam'] = [
            //     // Context is displayed as a String in the metadata, "in":"query", and is optional.
            //     'Context' => 'Morning',
            // ];
            // $formData = [
            //     'FormatType' => 'text',
            //     'SourceLanguage' => 'zh',
            //     'TargetLanguage' => 'en',
            //     'SourceText' => 'Hello',
            //     'Scene' => 'general',
            // ];
            // $str = self::formDataToString($formData);
            // $request['body'] = $str;
            // $request['headers']['content-type'] = 'application/x-www-form-urlencoded';
    
            // // An ROA-style API POST request.
            // $request = $this->createRequest('POST', '/clusters', 'cs.cn-beijing.aliyuncs.com', 'CreateCluster', '2015-12-15');
            // $bodyData = [
            //     'name' => 'test-cluster',
            //     'region_id' => 'cn-beijing',
            //     'cluster_type' => 'ExternalKubernetes',
            //     'vpcid' => 'vpc-2zeou1uod4ylaXXXXXXXX',
            //     'service_cidr' => '10.2.0.0/24',
            //     'security_group_id' => 'sg-2ze1a0rlgeo7XXXXXXXX',
            //     "vswitch_ids" => [
            //         "vsw-2zei30dhfldu8XXXXXXXX"
            //     ]
            // ];
            // $request['body'] = json_encode($bodyData, JSON_UNESCAPED_UNICODE);
            // $request['headers']['content-type'] = 'application/json; charset=utf-8';
    
            // // An ROA-style API GET request.
            // // If canonicalUri contains a path parameter, encode the path parameter by using rawurlencode({path parameter}).
            // $cluster_id = 'c930976b3b1fc4e02bc09831dXXXXXXXX';
            // $canonicalUri = sprintf("/clusters/%s/resources", rawurlencode($cluster_id));
            // $request = $this->createRequest('GET', $canonicalUri, 'cs.cn-beijing.aliyuncs.com', 'DescribeClusterResources', '2015-12-15');
            // $request['queryParam'] = [
            //     'with_addon_resources' => true,
            // ];
    
            // // An ROA-style API DELETE request.
            // $cluster_id = 'c930976b3b1fc4e02bc09831dXXXXXXXX';
            // $canonicalUri = sprintf("/clusters/%s", rawurlencode($cluster_id));
            // $request = $this->createRequest('DELETE', $canonicalUri, 'cs.cn-beijing.aliyuncs.com', 'DeleteCluster', '2015-12-15');
    
            $this->getAuthorization($request);
            // Call the API operation.
            $this->callApi($request);
        }
    
        private function createRequest($httpMethod, $canonicalUri, $host, $xAcsAction, $xAcsVersion)
        {
            $headers = [
                'host' => $host,
                'x-acs-action' => $xAcsAction,
                'x-acs-version' => $xAcsVersion,
                'x-acs-date' => gmdate('Y-m-d\TH:i:s\Z'),
                'x-acs-signature-nonce' => bin2hex(random_bytes(16)),
            ];
            return [
                'httpMethod' => $httpMethod,
                'canonicalUri' => $canonicalUri,
                'host' => $host,
                'headers' => $headers,
                'queryParam' => [],
                'body' => null,
            ];
        }
    
        private function getAuthorization(&$request)
        {
            $request['queryParam'] = $this->processObject($request['queryParam']);
            $canonicalQueryString = $this->buildCanonicalQueryString($request['queryParam']);
            $hashedRequestPayload = hash('sha256', $request['body'] ?? '');
            $request['headers']['x-acs-content-sha256'] = $hashedRequestPayload;
    
            if($this->SecurityToken){
                $request['headers']['x-acs-security-token'] = $this->SecurityToken;
            }
    
            $canonicalHeaders = $this->buildCanonicalHeaders($request['headers']);
            $signedHeaders = $this->buildSignedHeaders($request['headers']);
    
            $canonicalRequest = implode("\n", [
                $request['httpMethod'],
                $request['canonicalUri'],
                $canonicalQueryString,
                $canonicalHeaders,
                $signedHeaders,
                $hashedRequestPayload,
            ]);
    
            $hashedCanonicalRequest = hash('sha256', $canonicalRequest);
            $stringToSign = "{$this->ALGORITHM}\n$hashedCanonicalRequest";
    
            $signature = strtolower(bin2hex(hash_hmac('sha256', $stringToSign, $this->AccessKeySecret, true)));
            $authorization = "{$this->ALGORITHM} Credential={$this->AccessKeyId},SignedHeaders=$signedHeaders,Signature=$signature";
    
            $request['headers']['Authorization'] = $authorization;
        }
    
        private function callApi($request)
        {
            try {
                // Send the request by using cURL.
                $url = "https://" . $request['host'] . $request['canonicalUri'];
    
                // Add the request parameters to the URL.
                if (!empty($request['queryParam'])) {
                    $url .= '?' . http_build_query($request['queryParam']);
                }
    
                echo $url;
                // Initialize a cURL session.
                $ch = curl_init();
    
                // Set cURL options.
                curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Disable SSL certificate verification. Note that this reduces security and must not be used in a production environment. Not recommended.
                curl_setopt($ch, CURLOPT_URL, $url);
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // Return the content instead of printing it.
                curl_setopt($ch, CURLOPT_HTTPHEADER, $this->convertHeadersToArray($request['headers'])); // Add the request headers.
    
                // Set cURL options based on the request type.
                switch ($request['httpMethod']) {
                    case "GET":
                        break;
                    case "POST":
                        curl_setopt($ch, CURLOPT_POST, true);
                        curl_setopt($ch, CURLOPT_POSTFIELDS, $request['body']);
                        break;
                    case "DELETE":
                        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
                        break;
                    default:
                        echo "Unsupported HTTP method: " . $request['body'];
                        throw new Exception("Unsupported HTTP method");
                }
    
                // Send the request.
                $result = curl_exec($ch);
    
                // Check whether an error occurred.
                if (curl_errno($ch)) {
                    echo "Failed to send request: " . curl_error($ch);
                } else {
                    echo $result;
                }
    
            } catch (Exception $e) {
                echo "Error: " . $e->getMessage();
            } finally {
                // Close the cURL session.
                curl_close($ch);
            }
        }
    
        function formDataToString($formData)
        {
            $res = self::processObject($formData);
            return http_build_query($res);
        }
    
        function processObject($value)
        {
            // If the value is empty, no further processing is required.
            if ($value === null) {
                return;
            }
            $tmp = [];
            foreach ($value as $k => $v) {
                if (0 !== strpos($k, '_')) {
                    $tmp[$k] = $v;
                }
            }
            return self::flatten($tmp);
        }
    
        private static function flatten($items = [], $delimiter = '.', $prepend = '')
        {
            $flatten = [];
            foreach ($items as $key => $value) {
                $pos = \is_int($key) ? $key + 1 : $key;
    
                if (\is_object($value)) {
                    $value = get_object_vars($value);
                }
    
                if (\is_array($value) && !empty($value)) {
                    $flatten = array_merge(
                        $flatten,
                        self::flatten($value, $delimiter, $prepend . $pos . $delimiter)
                    );
                } else {
                    if (\is_bool($value)) {
                        $value = true === $value ? 'true' : 'false';
                    }
                    $flatten["$prepend$pos"] = $value;
                }
            }
            return $flatten;
        }
    
        private function convertHeadersToArray($headers)
        {
            $headerArray = [];
            foreach ($headers as $key => $value) {
                $headerArray[] = "$key: $value";
            }
            return $headerArray;
        }
    
        private function buildCanonicalQueryString($queryParams)
        {
    
            ksort($queryParams);
            // Build and encode query parameters
            $params = [];
            foreach ($queryParams as $k => $v) {
                if (null === $v) {
                    continue;
                }
                $str = rawurlencode($k);
                if ('' !== $v && null !== $v) {
                    $str .= '=' . rawurlencode($v);
                } else {
                    $str .= '=';
                }
                $params[] = $str;
            }
            return implode('&', $params);
        }
    
        private function buildCanonicalHeaders($headers)
        {
            // Sort headers by key and concatenate them
            uksort($headers, 'strcasecmp');
            $canonicalHeaders = '';
            foreach ($headers as $key => $value) {
                $canonicalHeaders .= strtolower($key) . ':' . trim($value) . "\n";
            }
            return $canonicalHeaders;
        }
    
        private function buildSignedHeaders($headers)
        {
            // Build the signed headers string
            $signedHeaders = array_keys($headers);
            sort($signedHeaders, SORT_STRING | SORT_FLAG_CASE);
            return implode(';', array_map('strtolower', $signedHeaders));
        }
    }
    
    $demo = new SignatureDemo();
    $demo->main();

    .NET

    O código de exemplo foi executado no .NET 8.0.302. Talvez seja necessário ajustar o código conforme seu ambiente real.

    using System.Globalization;
    using System.Net;
    using System.Net.Http.Headers;
    using System.Security.Cryptography;
    using System.Text;
    using System.Web;
    using Newtonsoft.Json;
    
    namespace SignatureDemo
    {
        public class Request
        {
            public string HttpMethod { get; private set; }
            public string CanonicalUri { get; private set; }
            public string Host { get; private set; }
            public string XAcsAction { get; private set; }
            public string XAcsVersion { get; private set; }
            public SortedDictionary<string, object> Headers { get; private set; }
            public byte[]? Body { get; set; }
            public Dictionary<string, object> QueryParam { get; set; }
    
            public Request(string httpMethod, string canonicalUri, string host, string xAcsAction, string xAcsVersion)
            {
                HttpMethod = httpMethod;
                CanonicalUri = canonicalUri;
                Host = host;
                XAcsAction = xAcsAction;
                XAcsVersion = xAcsVersion;
                Headers = [];
                QueryParam = [];
                Body = null;
                InitHeader();
            }
    
            private void InitHeader()
            {
                Headers["host"] = Host;
                Headers["x-acs-action"] = XAcsAction;
                Headers["x-acs-version"] = XAcsVersion;
                DateTime utcNow = DateTime.UtcNow;
                Headers["x-acs-date"] = utcNow.ToString("yyyy-MM-dd'T'HH:mm:ss'Z'", CultureInfo.InvariantCulture);
                Headers["x-acs-signature-nonce"] = Guid.NewGuid().ToString();
            }
        }
    
        public class Program
        {
            private static readonly string AccessKeyId = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_ID") ?? throw new InvalidOperationException("The ALIBABA_CLOUD_ACCESS_KEY_ID environment variable is not set.");
            private static readonly string AccessKeySecret = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_ACCESS_KEY_SECRET") ?? throw new InvalidOperationException("The ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable is not set.");
            private static readonly string? SecurityToken = Environment.GetEnvironmentVariable("ALIBABA_CLOUD_SECURITY_TOKEN");
            private const string Algorithm = "ACS3-HMAC-SHA256";
            private const string ContentType = "content-type";
    
            /**
            * This is a signature example. Replace the sample parameters in the main method with your actual values.
            * ROA-style and RPC-style APIs differ only in how the canonicalUri value is obtained. The rest of the process is the same.
            *
            * Obtain the request method (methods), request parameter name (name), request parameter type (type), and request parameter position (in) from the API metadata, and then encapsulate the parameters into SignatureRequest.
            * 1. If a request parameter is marked "in":"query" in the metadata, pass it in queryParam. Note: For an RPC-style API, this type of parameter can also be passed in the body, with content-type set to application/x-www-form-urlencoded. See example 3.
            * 2. If a request parameter is marked "in": "body" in the metadata, pass it in the body, with the MIME type set to application/octet-stream or application/json. For an RPC-style API, application/json is not recommended. Use example 3 instead.
            * 3. If a request parameter is marked "in": "formData" in the metadata, pass it in the body, with the MIME type set to application/x-www-form-urlencoded.
            */
            public static void Main(string[] args)
            {
                // RPC-style API request example 1: the request parameter is "in":"query".
                string httpMethod = "POST"; // The request method. Most RPC-style APIs support both POST and GET. POST is used in this example.
                string canonicalUri = "/"; // An RPC-style API has no resource path. Therefore, a forward slash (/) is used as the CanonicalURI.
                string host = "ecs.cn-hangzhou.aliyuncs.com"; // The service endpoint of the Alibaba Cloud service.
                string xAcsAction = "DescribeInstanceStatus"; // The API name.
                string xAcsVersion = "2014-05-26"; // The API version.
                var request = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion);
                // The request parameters of DescribeInstanceStatus are as follows:
                // RegionId is displayed as a String in the metadata, "in":"query", and is required.
                request.QueryParam["RegionId"] = "cn-hangzhou";
                // InstanceId is displayed as an array in the metadata, "in":"query", and is optional.
                List<string> instanceIds = ["i-bp10igfmnyttXXXXXXXX", "i-bp1incuofvzxXXXXXXXX", "i-bp1incuofvzxXXXXXXXX"];
                request.QueryParam["InstanceId"] = instanceIds;
    
                // // RPC-style API request example 2: the request parameter is "in":"body" (file upload scenario).
                // string httpMethod = "POST";
                // string canonicalUri = "/";
                // string host = "ocr-api.cn-hangzhou.aliyuncs.com";
                // string xAcsAction = "RecognizeGeneral";
                // string xAcsVersion = "2021-07-07";
                // var request = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion);
                // // The request parameter is marked "in": "body" in the metadata. Pass it in the body.
                // request.Body = File.ReadAllBytes(@"D:\test.png");
                // request.Headers["content-type"] = "application/octet-stream";
    
                // // RPC-style API request example 3: the request parameter is "in": "formData" or "in":"body" (non-file upload scenario).
                // string httpMethod = "POST";
                // string canonicalUri = "/";
                // string host = "mt.aliyuncs.com";
                // string xAcsAction = "TranslateGeneral";
                // string xAcsVersion = "2018-10-12";
                // var request = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion);
                // // The request parameters of TranslateGeneral are as follows:
                // // Context is displayed as a String in the metadata, "in":"query", and is optional.
                // request.QueryParam["Context"] = "Morning";
                // // Parameters such as FormatType, SourceLanguage, and TargetLanguage are marked "in":"formData" in the metadata.
                // var body = new Dictionary<string, object>
                // {
                //     { "FormatType", "text" },
                //     { "SourceLanguage", "zh" },
                //     { "TargetLanguage", "en" },
                //     { "SourceText", "Hello" },
                //     { "Scene", "general" },
                // };
                // var str = FormDataToString(body);
                // request.Body = Encoding.UTF8.GetBytes(str);
                // request.Headers[ContentType] = "application/x-www-form-urlencoded";
    
                // // An ROA-style API POST request.
                // String httpMethod = "POST";
                // String canonicalUri = "/clusters";
                // String host = "cs.cn-beijing.aliyuncs.com";
                // String xAcsAction = "CreateCluster";
                // String xAcsVersion = "2015-12-15";
                // Request request = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion);
                // // The request body. Use JsonConvert to convert the body into a JSON string.
                // var body = new SortedDictionary<string, object>
                // {
                //     { "name", "testDemo" },
                //     { "region_id", "cn-beijing" },
                //     { "cluster_type", "ExternalKubernetes" },
                //     { "vpcid", "vpc-2zeou1uod4ylaXXXXXXXX" },
                //     { "container_cidr", "10.0.0.0/8" },
                //     { "service_cidr", "172.16.1.0/20" },
                //     { "security_group_id", "sg-2ze1a0rlgeo7XXXXXXXX" },
                //     { "vswitch_ids", new List<string>{"vsw-2zei30dhfldu8XXXXXXXX"} },
                // };
                // string jsonBody = JsonConvert.SerializeObject(body, Formatting.None);
                // request.Body = Encoding.UTF8.GetBytes(jsonBody);
                // request.Headers[ContentType] = "application/json; charset=utf-8";
    
                // // An ROA-style API GET request.
                // String httpMethod = "GET";
                // // If canonicalUri contains a path parameter, encode the path parameter by using percentCode({path parameter}).
                // String canonicalUri = "/clusters/" + PercentCode("c81d501a467594eab873edbf2XXXXXXXX") + "/resources";
                // String host = "cs.cn-beijing.aliyuncs.com";
                // String xAcsAction = "DescribeClusterResources";
                // String xAcsVersion = "2015-12-15";
                // Request request = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion);
                // request.QueryParam["with_addon_resources"]=true;
    
                // // An ROA-style API DELETE request.
                // String httpMethod = "DELETE";
                // // If canonicalUri contains a path parameter, encode the path parameter by using percentCode({path parameter}).
                // String canonicalUri = "/clusters/" + PercentCode("c81d501a467594eab873edbf2XXXXXXXX");
                // String host = "cs.cn-beijing.aliyuncs.com";
                // String xAcsAction = "DeleteCluster";
                // String xAcsVersion = "2015-12-15";
                // Request request = new Request(httpMethod, canonicalUri, host, xAcsAction, xAcsVersion);
    
                GetAuthorization(request);
                // Call the API operation.
                var result = CallApiAsync(request);
                Console.WriteLine($"result:{result.Result}");
            }
    
            private static async Task<string?> CallApiAsync(Request request)
            {
                try
                {
                    // Declare httpClient.
                    using var httpClient = new HttpClient();
    
                    // Construct the URL.
                    string url = $"https://{request.Host}{request.CanonicalUri}";
                    var uriBuilder = new UriBuilder(url);
                    var query = new List<string>();
    
                    // Add the request parameters.
                    foreach (var entry in request.QueryParam.OrderBy(e => e.Key.ToLower()))
                    {
                        string value = entry.Value?.ToString() ?? "";
                        query.Add($"{entry.Key}={Uri.EscapeDataString(value)}");
                    }
    
                    uriBuilder.Query = string.Join("&", query);
                    Console.WriteLine(uriBuilder.Uri);
                    var requestMessage = new HttpRequestMessage
                    {
                        Method = new HttpMethod(request.HttpMethod),
                        RequestUri = uriBuilder.Uri,
                    };
    
                    // Set the request headers.
                    foreach (var entry in request.Headers)
                    {
                        if (entry.Key == "Authorization")
                        {
                            requestMessage.Headers.TryAddWithoutValidation("Authorization", entry.Value.ToString()); ;
                        }
                        else if (entry.Key == ContentType) // Must be consistent with the value defined in main.
                        {
                            continue;
                        }
                        else
                        {
                            requestMessage.Headers.Add(entry.Key, entry.Value.ToString());
                        }
                    }
    
                    if (request.Body != null)
                    {
                        HttpContent content = new ByteArrayContent(request.Body);
                        string contentType = request.Headers["content-type"].ToString();
                        content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType);
                        requestMessage.Content = content;
                    }
    
                    // Send the request.
                    HttpResponseMessage response = await httpClient.SendAsync(requestMessage);
                    // Read the response content.
                    string result = await response.Content.ReadAsStringAsync();
                    return result;
                }
                catch (UriFormatException e)
                {
                    Console.WriteLine("Invalid URI syntax");
                    Console.WriteLine(e.Message);
                    return null;
                }
                catch (Exception e)
                {
                    Console.WriteLine("Failed to send request");
                    Console.WriteLine(e);
                    return null;
                }
            }
    
            private static void GetAuthorization(Request request)
            {
                try
                {
                    // Flatten the query parameters whose values are of the List or Map type in queryParam.
                    request.QueryParam = FlattenDictionary(request.QueryParam);
    
                    // Step 1: Construct the canonical request.
                    StringBuilder canonicalQueryString = new();
                    foreach (var entry in request.QueryParam.OrderBy(e => e.Key.ToLower()))
                    {
                        if (canonicalQueryString.Length > 0)
                        {
                            canonicalQueryString.Append('&');
                        }
                        canonicalQueryString.Append($"{PercentCode(entry.Key)}={PercentCode(entry.Value?.ToString() ?? "")}");
                    }
    
                    byte[] requestPayload = request.Body ?? Encoding.UTF8.GetBytes("");
                    string hashedRequestPayload = Sha256Hash(requestPayload);
                    request.Headers["x-acs-content-sha256"] = hashedRequestPayload;
                    if (!string.IsNullOrEmpty(SecurityToken))
                    {
                        request.Headers["x-acs-security-token"] = SecurityToken;
                    }
    
                    StringBuilder canonicalHeaders = new();
                    StringBuilder signedHeadersSb = new();
                    foreach (var entry in request.Headers.OrderBy(e => e.Key.ToLower()))
                    {
                        if (entry.Key.StartsWith("x-acs-", StringComparison.CurrentCultureIgnoreCase) || entry.Key.Equals("host", StringComparison.OrdinalIgnoreCase) || entry.Key.Equals(ContentType, StringComparison.OrdinalIgnoreCase))
                        {
                            string lowerKey = entry.Key.ToLower();
                            string value = (entry.Value?.ToString() ?? "").Trim();
                            canonicalHeaders.Append($"{lowerKey}:{value}\n");
                            signedHeadersSb.Append($"{lowerKey};");
                        }
                    }
                    string signedHeaders = signedHeadersSb.ToString().TrimEnd(';');
                    string canonicalRequest = $"{request.HttpMethod}\n{request.CanonicalUri}\n{canonicalQueryString}\n{canonicalHeaders}\n{signedHeaders}\n{hashedRequestPayload}";
                    Console.WriteLine($"canonicalRequest:{canonicalRequest}");
    
                    // Step 2: Construct the string-to-sign.
                    string hashedCanonicalRequest = Sha256Hash(Encoding.UTF8.GetBytes(canonicalRequest));
                    string stringToSign = $"{Algorithm}\n{hashedCanonicalRequest}";
                    Console.WriteLine($"stringToSign:{stringToSign}");
    
                    // Step 3: Calculate the signature.
                    string signature = HmacSha256(AccessKeySecret, stringToSign);
    
                    // Step 4: Construct the Authorization header.
                    string authorization = $"{Algorithm} Credential={AccessKeyId},SignedHeaders={signedHeaders},Signature={signature}";
                    request.Headers["Authorization"] = authorization;
                    Console.WriteLine($"authorization:{authorization}");
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Failed to get authorization");
                    Console.WriteLine(ex.Message);
                }
            }
    
            private static string FormDataToString(Dictionary<string, object> formData)
            {
                Dictionary<string, object> tileMap = FlattenDictionary( formData);
    
                StringBuilder result = new StringBuilder();
                bool first = true;
                string symbol = "&";
    
                foreach (var entry in tileMap)
                {
                    string value = entry.Value?.ToString() ?? "";
                    if (!string.IsNullOrEmpty(value))
                    {
                        if (!first)
                        {
                            result.Append(symbol);
                        }
                        first = false;
                        result.Append(PercentCode(entry.Key));
                        result.Append("=");
                        result.Append(PercentCode(value));
                    }
                }
                return result.ToString();
            }
    
            private static Dictionary<string, object> FlattenDictionary(Dictionary<string, object> dictionary, string prefix = "")
            {
                var result = new Dictionary<string, object>();
                foreach (var kvp in dictionary)
                {
                    string key = string.IsNullOrEmpty(prefix) ? kvp.Key : $"{prefix}.{kvp.Key}";
    
                    if (kvp.Value is Dictionary<string, object> nestedDict)
                    {
                        var nestedResult = FlattenDictionary(nestedDict, key);
                        foreach (var nestedKvp in nestedResult)
                        {
                            result[nestedKvp.Key] = nestedKvp.Value;
                        }
                    }
                    else if (kvp.Value is List<string> list)
                    {
                        for (int i = 0; i < list.Count; i++)
                        {
                            result[$"{key}.{i + 1}"] = list[i];
                        }
                    }
                    else
                    {
                        result[key] = kvp.Value;
                    }
                }
                return result;
            }
    
            private static string HmacSha256(string key, string message)
            {
                using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key)))
                {
                    byte[] hashMessage = hmac.ComputeHash(Encoding.UTF8.GetBytes(message));
                    return BitConverter.ToString(hashMessage).Replace("-", "").ToLower();
                }
            }
    
            private static string Sha256Hash(byte[] input)
            {
                byte[] hashBytes = SHA256.HashData(input);
                return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
            }
    
            private static string PercentCode(string str)
            {
                if (string.IsNullOrEmpty(str))
                {
                    throw new ArgumentException("The input string cannot be null or empty.");
                }
                return Uri.EscapeDataString(str).Replace("+", "%20").Replace("*", "%2A").Replace("%7E", "~");
            }
        }
    }

    Rust

    O código de exemplo foi testado no rustc 1.82.0. Ajuste o código conforme necessário para o seu ambiente.

    Para executar o exemplo em Rust, adicione as seguintes dependências ao arquivo Cargo.toml.

    [dependencies]
    serde = { version = "1.0" }
    serde_json = "1.0"
    rand = "0.8"
    base64 = "0.21"
    sha2 = "0.10"
    chrono = "0.4"
    hmac = "0.12"
    hex = "0.4"
    reqwest = { version = "0.11", features = ["json"] }
    tokio = { version = "1", features = ["full"] }
    percent-encoding = "2.1"
    use core::str;
    use std::collections::{BTreeMap, HashMap};
    use std::env;
    use std::time::{SystemTime, SystemTimeError};
    use chrono::DateTime;
    use hmac::{Hmac, Mac};
    use percent_encoding::{NON_ALPHANUMERIC, utf8_percent_encode};
    use rand::Rng;
    use serde_json::{json, Value};
    use std::borrow::Cow;
    use reqwest::{
        Client,
        header::{HeaderMap, HeaderValue}, Method, Response, StatusCode,
    };
    use sha2::{Digest, Sha256};
    use base64::engine::general_purpose::STANDARD;
    use base64::Engine;
    
    // Generate x-acs-date.
    pub fn current_timestamp() -> Result<u64, SystemTimeError> {
        Ok(SystemTime::now()
            .duration_since(SystemTime::UNIX_EPOCH)?
            .as_secs())
    }
    // Perform URL encoding.
    pub fn percent_code(encode_str: &str) -> Cow<'_, str> {
        let encoded = utf8_percent_encode(encode_str, NON_ALPHANUMERIC)
            .to_string()
            .replace("+", "20%")
            .replace("%5F", "_")
            .replace("%2D", "-")
            .replace("%2E", ".")
            .replace("%7E", "~");
    
        Cow::Owned(encoded) // Return a Cow<str> that can hold a String or an &str.
    }
    
    fn flatten_target_ops(
        targets: Vec<HashMap<&str, &str>>,
        base_key: &str,
    ) -> Vec<(&'static str, &'static str)> {
        let mut result = Vec::new();
    
        for (idx, item) in targets.iter().enumerate() {
            let prefix = format!("{}.{}", base_key, idx + 1);
    
            for (&k, &v) in item {
                let key = format!("{}.{}", prefix, k);
                let key_static: &'static str = Box::leak(key.into_boxed_str());
                let value_static: &'static str = Box::leak(v.to_string().into_boxed_str());
    
                result.push((key_static, value_static));
            }
        }
    
        result
    }
    
    /// Calculate the SHA256 hash.
    pub fn sha256_hex(message: &str) -> String {
        let mut hasher = Sha256::new();
        hasher.update(message);
        format!("{:x}", hasher.finalize()).to_lowercase()
    }
    // HMAC SHA256
    pub fn hmac256(key: &[u8], message: &str) -> Result<Vec<u8>, String> {
        let mut mac = Hmac::<Sha256>::new_from_slice(key)
            .map_err(|e| format!("use data key on sha256 fail:{}", e))?;
        mac.update(message.as_bytes());
        let signature = mac.finalize();
        Ok(signature.into_bytes().to_vec())
    }
    // Generate the signature nonce.
    pub fn generate_random_string(length: usize) -> String {
        const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
        let mut rng = rand::thread_rng();
        (0..length)
            .map(|_| CHARSET[rng.gen_range(0..CHARSET.len())] as char)
            .collect()
    }
    pub fn generate_nonce() -> String {
        generate_random_string(32)
    }
    // Construct the canonical query parameters (encoded).
    pub fn build_sored_encoded_query_string(query_params: &[(&str, &str)]) -> String {
        let sorted_query_params: BTreeMap<_, _> = query_params.iter().copied().collect();
        let encoded_params: Vec<String> = sorted_query_params
            .into_iter()
            .map(|(k, v)| {
                let encoded_key = percent_code(k);
                let encoded_value = percent_code(v);
                format!("{}={}", encoded_key, encoded_value)
            })
            .collect();
        encoded_params.join("&")
    }
    // Read the response.
    pub async fn read_response(result: Response) -> Result<(StatusCode, String), String> {
        let status = result.status();
        let data = result.bytes().await.map_err(|e| format!("Read response body failed: {}", e))?;
        let res = match str::from_utf8(&data) {
            Ok(s) => s.to_string(),
            Err(_) => return Err("Body contains non UTF-8 characters".to_string()),
        };
        Ok((status, res))
    }
    // Define the value type of FormData.
    #[derive(Debug, Clone)]
    pub enum FormValue {
        String(String),
        Vec(Vec<String>),
        HashMap(HashMap<String, String>),
    }
    // Define an enum for the request body to handle the Json, Binary, and FormData body types in a unified way.
    pub enum RequestBody {
        Json(HashMap<String, Value>), // Json
        Binary(Vec<u8>), // Binary
        FormData(HashMap<String, FormValue>), //  FormData
        None,
    }
    // Canonicalize the request.
    pub async fn call_api(
        client: Client,
        method: Method,
        host: &str,
        canonical_uri: &str,
        query_params: &[(&str, &str)],
        action: &str,
        version: &str,
        body: RequestBody,
        access_key_id: &str,
        access_key_secret: &str,
    ) -> Result<String, String> {
    
        // Process the request body based on the body type and store the result in the body_content variable.
        let body_content = match &body {
            RequestBody::Json(body_map) => json!(body_map).to_string(),
            RequestBody::Binary(binary_data) => {
                STANDARD.encode(binary_data)
            },
            RequestBody::FormData(form_data) => {
                let params: Vec<String> = form_data
                .iter()
                .flat_map(|(k, v)| {
                    match v {
                        FormValue::String(s) => {
                            vec![format!("{}={}", percent_code(k), percent_code(&s))]
                        },
                        FormValue::Vec(vec) => {
                            vec.iter()
                                .map(|s| format!("{}={}", percent_code(k), percent_code(s)))
                                .collect::<Vec<_>>()
                        },
                        FormValue::HashMap(map) => {
                            map.iter()
                                .map(|(sk, sv)| format!("{}={}", percent_code(sk), percent_code(sv)))
                                .collect::<Vec<_>>()
                        },
                    }
                })
                .collect();
                params.join("&")
            },
            RequestBody::None => String::new(),
        };
    
        // Calculate x-acs-content-sha256 of the request body. Prepare x-acs-date, x-acs-signature-nonce, and the headers to be signed.
        let hashed_request_payload = if body_content.is_empty() {
            sha256_hex("")
        } else {
            sha256_hex(&body_content)
        };
        // x-acs-date
        let now_time = current_timestamp().map_err(|e| format!("Get current timestamp failed: {}", e))?;
        let datetime = DateTime::from_timestamp(now_time as i64, 0).ok_or_else(|| format!("Get datetime from timestamp failed: {}", now_time))?;
        let datetime_str = datetime.format("%Y-%m-%dT%H:%M:%SZ").to_string();
        // x-acs-signature-nonce
        let signature_nonce = generate_nonce();
        println!("Signature Nonce: {}", signature_nonce);
        // The headers to be signed.
        let sign_header_arr = &[
            "host",
            "x-acs-action",
            "x-acs-content-sha256",
            "x-acs-date",
            "x-acs-signature-nonce",
            "x-acs-version",
        ];
        let sign_headers = sign_header_arr.join(";");
        // 1. Construct the canonical headers.
        let mut headers = HeaderMap::new();
        headers.insert("Host", HeaderValue::from_str(host).unwrap());
        headers.insert("x-acs-action", HeaderValue::from_str(action).unwrap());
        headers.insert("x-acs-version", HeaderValue::from_str(version).unwrap());
        headers.insert("x-acs-date", HeaderValue::from_str(&datetime_str).unwrap());
        headers.insert("x-acs-signature-nonce", HeaderValue::from_str(&signature_nonce).unwrap());
        headers.insert("x-acs-content-sha256", HeaderValue::from_str(&hashed_request_payload).unwrap());
        // 2. Construct the headers to be signed.
        let canonical_query_string = build_sored_encoded_query_string(query_params); // Encode and concatenate the parameters.
        println!("CanonicalQueryString: {}", canonical_query_string);
        let canonical_request = format!(
            "{}\n{}\n{}\n{}\n\n{}\n{}",
            method.as_str(),
            canonical_uri,
            canonical_query_string,
            sign_header_arr.iter().map(|&header| format!("{}:{}", header, headers[header].to_str().unwrap())).collect::<Vec<_>>().join("\n"),
            sign_headers,
            hashed_request_payload
        );
        println!("Canonical Request: {}", canonical_request);
        // 3. Calculate the SHA-256 hash of the headers to be signed.
        let result = sha256_hex(&canonical_request);
        // 4. Construct the string-to-sign.
        let string_to_sign = format!("ACS3-HMAC-SHA256\n{}", result);
        // 5. Calculate the signature.
        let signature = hmac256(access_key_secret.as_bytes(), &string_to_sign)?;
        let data_sign = hex::encode(&signature);
        let auth_data = format!(
            "ACS3-HMAC-SHA256 Credential={},SignedHeaders={},Signature={}",
            access_key_id, sign_headers, data_sign
        );
        // 6. Construct the Authorization header.
        headers.insert("Authorization", HeaderValue::from_str(&auth_data).unwrap());
        // Construct the URL and append the request parameters.
        let url: String;
        if !query_params.is_empty() {
            url = format!("https://{}{}?{}", host, canonical_uri,canonical_query_string);
        } else {
            url = format!("https://{}{}", host, canonical_uri);
        }
        // Send the request.
        let response = send_request(
            &client,
            method,
            &url,
            headers,
            query_params,
            &body,
            &body_content,
        )
        .await?;
    
        // Read the response.
        let (_, res) = read_response(response).await?;
        Ok(res)
    }
    
    /// Send the request.
    async fn send_request(
        client: &Client,
        method: Method,
        url: &str,
        headers: HeaderMap,
        query_params: &[(&str, &str)],     // Receives the query parameters.
        body: &RequestBody,                // Determines the body data type.
        body_content: &str,                // Receives the body parameters when the body is not empty: FormData, Json, or Binary.
    ) -> Result<Response, String> {
        let mut request_builder = client.request(method.clone(), url);
        // Add the request headers.
        for (k, v) in headers.iter() {
            request_builder = request_builder.header(k, v.clone());
        }
         // Add the request body.
         match body {
            RequestBody::Binary(_) => {
                request_builder = request_builder.header("Content-Type", "application/octet-stream");
                request_builder = request_builder.body(body_content.to_string()); // Move the value here.
            }
            RequestBody::Json(_) => {
                // If the body is a map and is not empty, convert it into JSON, store it in the body_content variable, and set application/json; charset=utf-8.
                if !body_content.is_empty() {
                    request_builder = request_builder.body(body_content.to_string());
                    request_builder = request_builder.header("Content-Type", "application/json; charset=utf-8");
                }
            }
            RequestBody::FormData(_) => {
                // Process the form-data type and set content-type.
                if !body_content.is_empty() {
                request_builder = request_builder.header("Content-Type", "application/x-www-form-urlencoded");
                request_builder = request_builder.body(body_content.to_string());
                }
            }
            RequestBody::None => {
                request_builder = request_builder.body(String::new());
            }
        }
        // Build the request.
        let request = request_builder
            .build()
            .map_err(|e| format!("build request fail: {}", e))?;
        // Send the request.
        let response = client
            .execute(request)
            .await
            .map_err(|e| format!("execute request fail: {}", e))?;
        // Return the result.
        Ok(response)
    }
    
     /**
         *
         * This is a signature example. Replace the sample parameters in the main method with your actual values.
         * <p>
         * Obtain the request method (methods), request parameter name (name), request parameter type (type), and request parameter position (in) from the API metadata.
         * 1. If a request parameter is marked "in":"query" in the metadata, pass it in query_params. Note: For an RPC-style API, this type of parameter can also be passed in the body, with content-type set to application/x-www-form-urlencoded. See example 3.
         * 2. If a request parameter is marked "in": "body" in the metadata, pass it in the body, with the MIME type set to application/octet-stream or application/json. For an RPC-style API, application/json is not recommended. Use example 3 instead.
         * 2. If a request parameter is marked "in": "formData" in the metadata, pass it in the body, with the MIME type set to application/x-www-form-urlencoded.
    */
    #[tokio::main]
    async fn main() {
        // Create the HTTP client.
        let client = Client::new();
        // env::var() obtains the AccessKey ID and AccessKey secret from environment variables.
        let access_key_id = env::var("ALIBABA_CLOUD_ACCESS_KEY_ID").expect("Cannot get access key id.");
        let access_key_secret = env::var("ALIBABA_CLOUD_ACCESS_KEY_SECRET").expect("Cannot get access key id.");
        let access_key_id: &str = &access_key_id;
        let access_key_secret: &str = &access_key_secret;
    
        // RPC-style API request example 1: the request parameter is "in":"query".   POST
        let method = Method::POST; // The request method.
        let host = "ecs.cn-hangzhou.aliyuncs.com"; // endpoint
        let canonical_uri = "/"; // An RPC-style API has no resource path. Therefore, a forward slash (/) is used as the CanonicalURI.
        let action = "DescribeInstanceStatus"; // The API name.
        let version = "2014-05-26"; // The API version.
        let region_id = "cn-hangzhou";
        let instance_ids = vec![
            "i-bp11ht4XXXXXXXX",
            "i-bp16mazXXXXXXXX",
        ];
        let mut query: Vec<(&str, &str)> = Vec::new();
        query.push(("RegionId", region_id));
        for (index, instance_id) in instance_ids.iter().enumerate() {
            let key = format!("InstanceId.{}", index + 1);
            query.push((Box::leak(key.into_boxed_str()), instance_id));
        }
        // The query parameters.
        let query_params: &[(&str, &str)] = &query;
        // When the request body is empty.
        let body = RequestBody:: None;
    
        // RPC-style API with "in":"query" where the query parameters are of a complex type.  POST
        // let method = Method::POST; // The request method.
        // let host = "tds.cn-shanghai.aliyuncs.com"; // endpoint
        // let canonical_uri = "/"; // An RPC-style API has no resource path. Therefore, a forward slash (/) is used as the CanonicalURI.
        // let action = "AddAssetSelectionCriteria"; // The API name.
        // let version = "2018-12-03"; // The API version.
        // Define the parameters.
        // let mut target_op = HashMap::new();
        // target_op.insert("Operation", "add");
        // target_op.insert("Target", "i-2ze1j7ocdXXXXXXXX");
        // Define the TargetOperationList parameter, whose collection contains items of the map type.
        // let target_operation_list = vec![target_op];
        // Flatten the parameters.
        // let mut query = flatten_target_ops(target_operation_list, "TargetOperationList");
        // Normal parameters.
        // query.push(("SelectionKey", "85a561b7-27d5-47ad-a0ec-XXXXXXXX"));
        // let query_params: &[(&str, &str)] = &query;
        // let body = RequestBody:: None;
    
        // RPC-style API request example 2: the request parameter is "in":"body" (file upload scenario).  POST
        // let method = Method::POST; // The request method.
        // let host = "ocr-api.cn-hangzhou.aliyuncs.com";
        // let canonical_uri = "/";
        // let action = "RecognizeGeneral";
        // let version = "2021-07-07";
        // The request parameter is "in":"body" of the binary file type.
        // let binary_data = std::fs::read("<FILE_PATH>").expect("Failed to read the file."); // Replace <FILE_PATH> with the actual file path.
        // When the body is of the binary type.
        // let body = RequestBody::Binary(binary_data);
        // The query parameters are empty.
        // let query_params = &[];
    
        // RPC-style API request example 3: the request parameter is "in": "formData" or "in":"body" (non-file upload scenario).  POST
        // let method = Method::POST; // The request method.
        // let host = "mt.aliyuncs.com";
        // let canonical_uri = "/";
        // let action = "TranslateGeneral";
        // let version = "2018-10-12";
        // // Parameters such as FormatType, SourceLanguage, and TargetLanguage are marked "in":"formData" in the metadata.
        // let mut form_data = HashMap::new();  // The body type is FormData(HashMap<String, FormValue>). FormValue supports Vec<String>, HashSet<String>, HashMap<String, String>, and more. You can add more types to the FormValue enum.
        // form_data.insert(String::from("FormatType"),FormValue::String(String::from("text")));
        // form_data.insert(String::from("SourceLanguage"),FormValue::String(String::from("zh")));
        // form_data.insert(String::from("TargetLanguage"),FormValue::String(String::from("en")));
        // form_data.insert(String::from("SourceText"),FormValue::String(String::from("Hello")));
        // form_data.insert(String::from("Scene"),FormValue::String(String::from("general")));
        // The query parameters.
        // let query_params = &[("Context", "Morning")];
        // When the body is of the FormData type, "in":"formdata".
        // let body = RequestBody::FormData(form_data);
    
        // ROA-style API POST request. API: CreateCluster, which creates a cluster.
        // Define the API request constants.
        // let method = Method::POST; // The request method.
        // let host = "cs.cn-hangzhou.aliyuncs.com";
        // let canonical_uri = "/clusters";
        // let action = "CreateCluster";
        // let version = "2015-12-15";
        // Set the request body parameters.
        // let mut body_json = HashMap::new();  //  The body type is Json(HashMap<String, Value>). Value supports the following types: Value::String("test".to_string()) // String  Value::Number(serde_json::Number::from(42)) // Number  Value::Bool(true) // Boolean  Value::Null // Null  Value::Array(vec![Value::from(1), Value::from(2), Value::from(3)]) //Array json!({"nested_key": "nested_value"})
        // body_json.insert(String::from("name"),json!("test-cluster"));
        // body_json.insert(String::from("region_id"),json!("cn-hangzhou"));
        // body_json.insert(String::from("cluster_type"),json!("ExternalKubernetes"));
        // body_json.insert(String::from("vpcid"),json!("vpc-2zeou1uodXXXXXXXX"));
        // body_json.insert(String::from("container_cidr"),json!("10.X.X.X/X"));
        // body_json.insert(String::from("service_cidr"),json!("10.X.X.X/X"));
        // body_json.insert(String::from("security_group_id"),json!("sg-2ze1a0rlgXXXXXXXX"));
        // body_json.insert(
        //     String::from("vswitch_ids"),
        //     Value::Array(vec![
        //         Value::from("vsw-2zei30dhflXXXXXXXX"),
        //         Value::from("vsw-2zei30dhflXXXXXXXX"),
        //         Value::from("vsw-2zei30dhflXXXXXXXX"),
        //     ]),
        // );
        // The query parameters are empty.
        // let query_params = &[];
        // When the body is of the Json type.
        // let body = RequestBody::Json(body_json);
    
        // ROA-style API GET request. API: DescribeClusterResources, which queries the resources associated with a specified cluster.
        // let method = Method::GET; // The request method.
        // let host = "cs.cn-hangzhou.aliyuncs.com"; // endpoint
        // // Concatenate the resource path.
        // let uri = format!("/clusters/{}/resources", percent_code("ce196d21571a64be9XXXXXXXX").as_ref());
        // let canonical_uri = uri.as_str(); // Convert the resource path into the &str type.
        // let action = "DescribeClusterResources";   // The API name.
        // let version = "2015-12-15"; // The API version.
        // // Set the query parameters.
        // let query_params = &[("with_addon_resources", if true { "true" } else { "false" })];  // "true" or "false"
        // // Set the body parameters to empty.
        // let body = RequestBody:: None;
    
        // ROA-style API DELETE request. API: DeleteCluster, which deletes a pay-as-you-go cluster.
        // let method = Method::DELETE;
        // let host = "cs.cn-hangzhou.aliyuncs.com";
        // let uri = format!("/clusters/{}", percent_code("ce0138ff31ad044f8XXXXXXXX").as_ref());
        // let canonical_uri = uri.as_str(); // Convert the resource path into the &str type.
        // let action = "DeleteCluster";
        // let version = "2015-12-15";
        // // The query parameters.
        // let query_params = &[];
        // // When the body parameters are empty.
        // let body = RequestBody:: None;
    
        // The SendSms API operation.
        // let method = Method::POST; // The request method.
        // let host = "dysmsapi.aliyuncs.com"; // endpoint
        // let canonical_uri = "/"; // An RPC-style API has no resource path. Therefore, a forward slash (/) is used as the CanonicalURI.
        // let action = "SendSms"; // The API name.
        // let version = "2017-05-25"; // The API version.
        // let mut query: Vec<(&str, &str)> = Vec::new();
        // query.push(("PhoneNumbers", "<YOUR_PHONENUMBERS>"));
        // query.push(("TemplateCode", "<YOUR_TEMPLATECODE>"));
        // query.push(("SignName", "<YOUR_SIGNNAME>"));
        // query.push(("TemplateParam", "<YOUR_TEMPLATEPARAM>"));
        // // The query parameters.
        // let query_params: &[(&str, &str)] = &query;
        // // When the request body is empty.
        // let body = RequestBody:: None;
    
        // Send the request.
        match call_api(
            client.clone(),
            method,                                                  // The API request method: POST, GET, or DELETE.
            host,                                                    // The API endpoint.
            canonical_uri,                                           // The API resource path.
            query_params,                                            // The "in":"query" query parameters.
            action,                                                  // The API name.
            version,                                                 // The API version.
            body,                                                    // The "in":"body" request body parameters. The Json, FormData, and Binary types are supported.
            access_key_id,
            access_key_secret,
        )
        .await {
            Ok(response) => println!("Response: {}", response),
            Err(error) => eprintln!("Error: {}", error),
        }
    }

    Shell

    #!/bin/bash
    
    accessKey_id="<YOUR-ACCESSKEY-ID>"
    accessKey_secret="<YOUR-ACCESSKEY-SECRET>"
    algorithm="ACS3-HMAC-SHA256"
    
    # The request parameters. Modify this part based on your actual scenario.
    httpMethod="POST"
    host="dns.aliyuncs.com"
    queryParam=("DomainName=example.com" "RRKeyWord=@")
    action="DescribeDomainRecords"
    version="2015-01-09"
    canonicalURI="/"
    # A parameter of the body or formdata type is passed in the body.
    # Parameter of the body type: the body value is a JSON string, such as "{'key1':'value1','key2':'value2'}", and you must add content-type:application/json; charset=utf-8 to the signed headers.
    # If the parameter of the body type is a binary file: you do not need to modify the body. Add content-type:application/octet-stream to the signed headers and add the --data-binary parameter to curl_command.
    # Parameter of the formdata type: the body parameter format is "key1=value1&key2=value2", and you must add content-type:application/x-www-form-urlencoded to the signed headers.
    body=""
    
    # The UTC time in the ISO 8601 standard.
    utc_timestamp=$(date +%s)
    utc_date=$(date -u -d @${utc_timestamp} +"%Y-%m-%dT%H:%M:%SZ")
    # The x-acs-signature-nonce random number.
    random=$(uuidgen | sed 's/-//g')
    
    # The signed headers.
    headers="host:${host}
    x-acs-action:${action}
    x-acs-version:${version}
    x-acs-date:${utc_date}
    x-acs-signature-nonce:${random}"
    
    # The URL encoding function.
    urlencode() {
        local string="${1}"
        local strlen=${#string}
        local encoded=""
        local pos c o
    
        for (( pos=0 ; pos<strlen ; pos++ )); do
            c=${string:$pos:1}
            case "$c" in
                [-_.~a-zA-Z0-9] ) o="${c}" ;;
                * )               printf -v o '%%%02X' "'$c"
            esac
            encoded+="${o}"
        done
        echo "${encoded}"
    }
    
    # Step 1: Construct the canonical request.
    # Flatten all parameters in queryParam.
    newQueryParam=()
    
    # Traverse each original parameter.
    for param in "${queryParam[@]}"; do
        # Check whether the parameter contains an equal sign to determine whether it is a key-value pair.
        if [[ "$param" == *"="* ]]; then
            # Split the key and the value.
            IFS='=' read -r key value <<< "$param"
    
            # URL-encode the value.
            value=$(urlencode "$value")
    
            # Check whether the value is a list by looking for parentheses.
            if [[ "$value" =~ ^\(.+\)$ ]]; then
                # Remove the parentheses on both sides.
                value="${value:1:-1}"
    
                # Split the value list by using IFS.
                IFS=' ' read -ra values <<< "$value"
    
                # Add an index for each value.
                index=1
                for val in "${values[@]}"; do
                    # Remove the double quotation marks.
                    val="${val%\"}"
                    val="${val#\"}"
    
                    # Add the value to the new array.
                    newQueryParam+=("$key.$index=$val")
                    ((index++))
                done
            else
                # If the value is not a list, add it directly.
                newQueryParam+=("$key=$value")
            fi
        else
            # If no equal sign exists, keep the parameter as it is.
            newQueryParam+=("$param")
        fi
    done
    
    # Process and sort the new query parameters.
    sortedParams=()
    declare -A paramsMap
    for param in "${newQueryParam[@]}"; do
        IFS='=' read -r key value <<< "$param"
        paramsMap["$key"]="$value"
    done
    # Sort the parameters by key.
    for key in $(echo ${!paramsMap[@]} | tr ' ' '\n' | LC_ALL=C sort); do
        sortedParams+=("$key=${paramsMap[$key]}")
    done
    
    # 1.1 Construct the canonical query string.
    canonicalQueryString=""
    first=true
    for item in "${sortedParams[@]}"; do
        [ "$first" = true ] && first=false || canonicalQueryString+="&"
        # Check whether an equal sign exists.
        if [[ "$item" == *=* ]]; then
            canonicalQueryString+="$item"
        else
            canonicalQueryString+="$item="
        fi
    done
    
    # 1.2 Process the request body.
    hashedRequestPayload=$(echo -n "$body" | openssl dgst -sha256 | awk '{print $2}')
    headers="${headers}
    x-acs-content-sha256:$hashedRequestPayload"
    
    # 1.3 Construct the canonical headers.
    canonicalHeaders=$(echo "$headers" | grep -E '^(host|content-type|x-acs-)' | while read line; do
        key=$(echo "$line" | cut -d':' -f1 | tr '[:upper:]' '[:lower:]')
        value=$(echo "$line" | cut -d':' -f2-)
        echo "${key}:${value}"
    done | sort | tr '\n' '\n')
    
    signedHeaders=$(echo "$headers" | grep -E '^(host|content-type|x-acs-)' | while read line; do
        key=$(echo "$line" | cut -d':' -f1 | tr '[:upper:]' '[:lower:]')
        echo "$key"
    done | sort | tr '\n' ';' | sed 's/;$//')
    
    # 1.4 Construct the canonical request.
    canonicalRequest="${httpMethod}\n${canonicalURI}\n${canonicalQueryString}\n${canonicalHeaders}\n\n${signedHeaders}\n${hashedRequestPayload}"
    echo -e "canonicalRequest=${canonicalRequest}"
    echo "+++++++++++++++++++++++++++++++++++++++++++++++++++"
    
    str=$(echo "$canonicalRequest" | sed 's/%/%%/g')
    hashedCanonicalRequest=$(printf "${str}" | openssl sha256 -hex | awk '{print $2}')
    # Step 2: Construct the string-to-sign.
    stringToSign="${algorithm}\n${hashedCanonicalRequest}"
    echo -e "stringToSign=$stringToSign"
    echo "+++++++++++++++++++++++++++++++++++++++++++++++++++"
    
    # Step 3: Calculate the signature.
    signature=$(printf "${stringToSign}" | openssl dgst -sha256 -hmac "${accessKey_secret}" | sed 's/^.* //')
    echo -e "signature=${signature}"
    echo "+++++++++++++++++++++++++++++++++++++++++++++++++++"
    
    # Step 4: Construct the Authorization header.
    authorization="${algorithm} Credential=${accessKey_id},SignedHeaders=${signedHeaders},Signature=${signature}"
    echo -e "authorization=${authorization}"
    
    # Construct the curl command.
    url="https://$host$canonicalURI"
    curl_command="curl -X $httpMethod '$url?$canonicalQueryString'"
    
    # Add the request headers.
    IFS=$'\n'  # Set the line feed as the new IFS.
    for header in $headers; do
        curl_command="$curl_command -H '$header'"
    done
    curl_command+=" -H 'Authorization:$authorization'"
    # If the parameter of the body type is a binary file, comment out the following line of code.
    curl_command+=" -d '$body'"
    # If the parameter of the body type is a binary file, uncomment the following line of code.
    #curl_command+=" --data-binary @"/root/001.png" "
    
    echo "$curl_command"
    # Run the curl command.
    eval "$curl_command"

    Linguagem C

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    #include <stdarg.h>
    #include <stdint.h>
    #include <openssl/hmac.h>
    #include <openssl/evp.h>
    #include <openssl/sha.h>
    #include <openssl/rand.h>
    #include <curl/curl.h>
    
    // getenv() obtains the AccessKey ID and AccessKey secret from environment variables.
    #define ACCESS_KEY_ID getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
    #define ACCESS_KEY_SECRET getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
    #define ALGORITHM "ACS3-HMAC-SHA256"
    #define BUFFER_SIZE 4096
    
    // The struct that is used for sorting.
    typedef struct {
        char key[256];
        char value[256];
    } KeyValuePair;
    
    // The comparison function, which sorts keys in lexicographic order.
    int compare_pairs(const void *a, const void *b) {
        return strcmp(((const KeyValuePair *)a)->key, ((const KeyValuePair *)b)->key);
    }
    
    // URL encoding.
    char* percentEncode(const char* str) {
        if (str == NULL) {
            fprintf(stderr, "The input string cannot be null\n");
            return NULL;
        }
        size_t len = strlen(str);
        char* encoded = (char*)malloc(len * 3 + 1);
        if (encoded == NULL) {
            fprintf(stderr, "Failed to allocate memory\n");
            free(encoded);
            return NULL;
        }
        char* ptr = encoded;
        for (size_t i = 0; i < len; i++) {
            unsigned char c = (unsigned char)str[i];
            if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
                *ptr++ = c;
            } else {
                ptr += sprintf(ptr, "%%%02X", c);
            }
        }
        *ptr = '\0';
        char* finalEncoded = malloc(strlen(encoded) + 1);
        if (finalEncoded) {
            char* fptr = finalEncoded;
            for (size_t j = 0; j < strlen(encoded); j++) {
                if (encoded[j] == '+') {
                    strcpy(fptr, "%20");
                    fptr += 3;
                } else if (encoded[j] == '*') {
                    strcpy(fptr, "%2A");
                    fptr += 3;
                } else if (encoded[j] == '~') {
                    *fptr++ = '~';
                } else {
                    *fptr++ = encoded[j];
                }
            }
            *fptr = '\0';
        }
    
        free(encoded);
        return finalEncoded;
    }
    
    /**
     * @brief URL-encodes the query parameters, sorts them in lexicographic order, and generates the canonical query string.
     * @param query_params The original query parameter string, in the "key1=value1&key2=value2" format.
     * @return char* The sorted and encoded canonical query string. The caller must free the memory.
     */
    char* generate_sorted_encoded_query(const char* query_params) {
        if (query_params == NULL || strlen(query_params) == 0) {
            return strdup(""); // Return an empty string if the parameter is empty.
        }
    
        KeyValuePair pairs[100]; // Up to 100 key-value pairs are supported.
        int pair_count = 0;
    
        char* copy = strdup(query_params);
        if (!copy) {
            fprintf(stderr, "Failed to allocate memory\n");
            return NULL;
        }
    
        char* token = NULL;
        char* saveptr = NULL;
        token = strtok_r(copy, "&", &saveptr);
    
        while (token != NULL && pair_count < 100) {
            char* eq = strchr(token, '=');
            if (eq) {
                size_t key_len = eq - token;
                char key[256], value[256];
    
                strncpy(key, token, key_len);
                key[key_len] = '\0';
    
                const char* val = eq + 1;
                strncpy(value, val, sizeof(value) - 1);
                value[sizeof(value) - 1] = '\0';
    
                char* encoded_key = percentEncode(key);
                char* encoded_value = percentEncode(value);
    
                strncpy(pairs[pair_count].key, encoded_key, sizeof(pairs[pair_count].key));
                strncpy(pairs[pair_count].value, encoded_value, sizeof(pairs[pair_count].value));
                pair_count++;
    
                free(encoded_key);
                free(encoded_value);
            }
            token = strtok_r(NULL, "&", &saveptr);
        }
    
        free(copy);
    
        // Sort by key.
        qsort(pairs, pair_count, sizeof(KeyValuePair), compare_pairs);
    
        // Concatenate the sorted query string.
        char* query_sorted = malloc(BUFFER_SIZE);
        if (!query_sorted) {
            fprintf(stderr, "Failed to allocate memory\n");
            return NULL;
        }
        query_sorted[0] = '\0';
    
        for (int i = 0; i < pair_count; ++i) {
            if (i == 0) {
                snprintf(query_sorted, BUFFER_SIZE, "%s=%s", pairs[i].key, pairs[i].value);
            } else {
                char temp[512];
                snprintf(temp, sizeof(temp), "&%s=%s", pairs[i].key, pairs[i].value);
                strncat(query_sorted, temp, BUFFER_SIZE - strlen(query_sorted) - 1);
            }
        }
    
        return query_sorted;
    }
    
    // HMAC-SHA256 calculation.
    void hmac256(const char *key, const char *message, char *output) {
        unsigned char hmac[SHA256_DIGEST_LENGTH];
        unsigned int result_len;
        HMAC(EVP_sha256(), key, strlen(key), (unsigned char *)message, strlen(message), hmac, &result_len);
        for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
            sprintf(output + (i * 2), "%02x", hmac[i]);
        }
        output[SHA256_DIGEST_LENGTH * 2] = '\0';
    }
    // Calculate the SHA-256 hash.
    void sha256_hex(const char *input, char *output) {
        unsigned char hash[SHA256_DIGEST_LENGTH];
        SHA256((unsigned char *)input, strlen(input), hash);
        for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
            sprintf(output + (i * 2), "%02x", hash[i]);
        }
        output[SHA256_DIGEST_LENGTH * 2] = '\0';
    }
    // Used to generate x-acs-signature-nonce.
    void generate_uuid(char *uuid, size_t size) {
        if (size < 37) {
            fprintf(stderr, "Buffer size too small for UUID\n");
            return;
        }
        unsigned char random_bytes[16];
        RAND_bytes(random_bytes, sizeof(random_bytes));
        random_bytes[6] &= 0x0f; // Keep the high 4 bits.
        random_bytes[6] |= 0x40; // Set the version to 4.
        random_bytes[8] &= 0x3f; // Keep the high 2 bits.
        random_bytes[8] |= 0x80; // Set the variant to 10xx.
        snprintf(uuid, size,
                 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
                 random_bytes[0], random_bytes[1], random_bytes[2], random_bytes[3],
                 random_bytes[4], random_bytes[5], random_bytes[6], random_bytes[7],
                 random_bytes[8], random_bytes[9], random_bytes[10], random_bytes[11],
                 random_bytes[12], random_bytes[13], random_bytes[14], random_bytes[15]);
    }
    // Upload a file.
    size_t read_file(const char *file_path, char **buffer) {
        FILE *file = fopen(file_path, "rb");
        if (!file) {
            fprintf(stderr, "Cannot open file %s\n", file_path);
            return 0; // Failed to read the file.
        }
        fseek(file, 0, SEEK_END);
        size_t file_size = ftell(file);
        fseek(file, 0, SEEK_SET);
    
        *buffer = (char *)malloc(file_size);
        if (!*buffer) {
            fprintf(stderr, "Failed to allocate memory for file buffer\n");
            fclose(file);
            return 0; // Failed to read the file.
        }
        fread(*buffer, 1, file_size, file);
        fclose(file);
        return file_size; // Return the number of bytes that are read.
    }
    // Calculate the Authorization header.
    char* get_authorization(const char *http_method, const char *canonical_uri, const char *host,
                           const char *x_acs_action, const char *x_acs_version, const char *query_params,
                           const char *body, char *authorization_header,
                            char *hashed_payload, char *x_acs_date, char *uuid) {
        // Prepare x-acs-signature-nonce, x-acs-date, x-acs-content-sha256, and the string-to-sign.
        generate_uuid(uuid, 37);
        // The format of x-acs-date is yyyy-MM-ddTHH:mm:ssZ, for example, 2025-04-17T07:19:10Z.
        time_t now = time(NULL);
        struct tm *utc_time = gmtime(&now);
        strftime(x_acs_date, 64, "%Y-%m-%dT%H:%M:%SZ", utc_time);
        // The string-to-sign.
        char signed_headers[] = "host;x-acs-action;x-acs-content-sha256;x-acs-date;x-acs-signature-nonce;x-acs-version";
        // x-acs-content-sha256
        sha256_hex(body ? body : "", hashed_payload);
        printf("Generated x-acs-content-sha256: %s\n", hashed_payload);
        // 1. Construct the canonical headers.
        char canonical_headers[BUFFER_SIZE];
        snprintf(canonical_headers, sizeof(canonical_headers),
                 "host:%s\nx-acs-action:%s\nx-acs-content-sha256:%s\nx-acs-date:%s\nx-acs-signature-nonce:%s\nx-acs-version:%s",
                  host, x_acs_action, hashed_payload, x_acs_date, uuid, x_acs_version);
        printf("Canonical Headers:\n%s\n", canonical_headers);
    
        // 2. Construct the headers to be signed.
        // Sort and encode the query parameters.
        char* sorted_query_params = generate_sorted_encoded_query(query_params);
        if (!sorted_query_params) {
          fprintf(stderr, "Failed to generate the sorted query string\n");
          return NULL;
        }
        char canonical_request[BUFFER_SIZE];
        snprintf(canonical_request, sizeof(canonical_request),
             "%s\n%s\n%s\n%s\n\n%s\n%s",
             http_method,
             canonical_uri,
             sorted_query_params ? sorted_query_params : "",
             canonical_headers,
             signed_headers,
             hashed_payload);
        printf("Canonical Request:\n%s\n", canonical_request);
    
        // 3. Calculate the SHA-256 hash of the canonical request.
        char hashed_canonical_request[SHA256_DIGEST_LENGTH * 2 + 1];
        sha256_hex(canonical_request, hashed_canonical_request);
        printf("hashedCanonicalRequest: %s\n", hashed_canonical_request);
        // 4. Construct the string-to-sign.
        char string_to_sign[BUFFER_SIZE];
        snprintf(string_to_sign, sizeof(string_to_sign), "%s\n%s", ALGORITHM, hashed_canonical_request);
        printf("stringToSign:\n%s\n", string_to_sign);
        // 5. Calculate the signature.
        char signature[SHA256_DIGEST_LENGTH * 2 + 1];
        hmac256(ACCESS_KEY_SECRET, string_to_sign, signature);
        printf("Signature: %s\n", signature);
        // 6. Construct the Authorization header.
        snprintf(authorization_header, BUFFER_SIZE,
                 "%s Credential=%s,SignedHeaders=%s,Signature=%s",
                 ALGORITHM, ACCESS_KEY_ID, signed_headers, signature);
        printf("Authorization: %s\n", authorization_header);
    
        return sorted_query_params;
    }
    // Send the request.
    void call_api(const char *http_method, const char *canonical_uri, const char *host,
                  const char *x_acs_action, const char *x_acs_version, const char *query_params,
                  const char *body,const char *content_type, size_t body_length) {
        // Obtain the parameter values that are required for signature calculation.
        char authorization_header[BUFFER_SIZE];
        char hashed_payload[SHA256_DIGEST_LENGTH * 2 + 1];
        char x_acs_date[64];
        char uuid[37];
        // 1. Initialize curl.
        CURL *curl = curl_easy_init();
        if (!curl) {
            fprintf(stderr, "curl_easy_init() failed\n");
            goto cleanup;
        }
        // 2. Calculate the signature. The sorted and encoded query parameters are returned.
        char *signed_query_params = get_authorization(http_method, canonical_uri, host, x_acs_action, x_acs_version, query_params, body, authorization_header, hashed_payload, x_acs_date, uuid);
        // 3. Add the request parameters.
        char url[BUFFER_SIZE];
        if (signed_query_params && strlen(signed_query_params) > 0) {
            snprintf(url, sizeof(url), "https://%s%s?%s", host, canonical_uri, signed_query_params);
        } else {
            snprintf(url, sizeof(url), "https://%s%s", host, canonical_uri);
        }
        printf("Request URL: %s\n", url);
        // Free the memory.
        if (signed_query_params) {
            free(signed_query_params); // Free the memory.
        }
    
        // 4. Add the request headers.
        struct curl_slist *headers = NULL;
        char header_value[BUFFER_SIZE];
        snprintf(header_value, sizeof(header_value), "Content-Type: %s", content_type);
        headers = curl_slist_append(headers, header_value);
        snprintf(header_value, sizeof(header_value), "Authorization: %s", authorization_header);
        headers = curl_slist_append(headers, header_value);
        snprintf(header_value, sizeof(header_value), "host: %s", host);
        headers = curl_slist_append(headers, header_value);
        snprintf(header_value, sizeof(header_value), "x-acs-action: %s", x_acs_action);
        headers = curl_slist_append(headers, header_value);
        snprintf(header_value, sizeof(header_value), "x-acs-content-sha256: %s", hashed_payload);
        headers = curl_slist_append(headers, header_value);
        snprintf(header_value, sizeof(header_value), "x-acs-date: %s", x_acs_date);
        headers = curl_slist_append(headers, header_value);
        snprintf(header_value, sizeof(header_value), "x-acs-signature-nonce: %s", uuid);
        headers = curl_slist_append(headers, header_value);
        snprintf(header_value, sizeof(header_value), "x-acs-version: %s", x_acs_version);
        headers = curl_slist_append(headers, header_value);
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, http_method);
        curl_easy_setopt(curl, CURLOPT_URL, url);
        // Other curl settings: disable SSL verification and enable debugging information.
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
        // 5. Add the request body.
        if (body) {
            curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body_length);
            if (strcmp(content_type, "application/octet-stream") == 0) {
                curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
            } else if (strcmp(content_type, "application/x-www-form-urlencoded") == 0) {
                curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
            } else if (strcmp(content_type, "application/json; charset=utf-8") == 0) {
                curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
            }
        }
        printf("RequestBody:%s\n",body);
        // 6. Send the request.
        CURLcode res = curl_easy_perform(curl);
        if (res != CURLE_OK) {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
            goto cleanup;
        }
    cleanup:
        if (headers) curl_slist_free_all(headers);
        if (curl) curl_easy_cleanup(curl);
    }
    /**
    *
         * This is a signature example. Replace the sample parameters in the main method with your actual values.
         * <p>
         * Obtain the request method (methods), request parameter name (name), request parameter type (type), and request parameter position (in) from the API metadata.
         * 1. If a request parameter is marked "in":"query" in the metadata, pass it in query_params. Note: For an RPC-style API, this type of parameter can also be passed in the body, with content-type set to application/x-www-form-urlencoded. See example 3.
         * 2. If a request parameter is marked "in": "body" in the metadata, pass it in the body, with the MIME type set to application/octet-stream or application/json. For an RPC-style API, application/json is not recommended. Use example 3 instead.
         * 2. If a request parameter is marked "in": "formData" in the metadata, pass it in the body, with the MIME type set to application/x-www-form-urlencoded.
    */
    int main() {
        // Set the response format to UTF-8.
        SetConsoleOutputCP(CP_UTF8);
        srand((unsigned int)time(NULL));
    
        /**
          * RPC-style API request example: the request parameter is "in":"query" and the query parameters are of a complex type.
        */
        const char *http_method = "POST";
        const char *canonical_uri = "/";
        const char *host = "tds.cn-shanghai.aliyuncs.com";
        const char *x_acs_action = "AddAssetSelectionCriteria";
        const char *x_acs_version = "2018-12-03";
    
        // Define the SelectionKey parameter of the string type.
        const char *selection_key = "85a561b7-27d5-47ad-a0ec-XXXXXXXX";
        // Define the TargetOperationList parameter, which is a collection of target objects. You can add more items.
        struct {
            const char *operation;
            const char *target;
        } targetOperation_list[] = {
            {"add", "i-2ze1j7ocdg9XXXXXXXX"},
            // You can add more items.
            // {"add", "i-abc123xyzXXXXX"},
        };
    
        int count = sizeof(targetOperation_list) / sizeof(targetOperation_list[0]);
        KeyValuePair pairs[100]; // Store the original keys and values.
        int pair_count = 0;
    
        for (int i = 0; i < count; ++i) {
          char op_key[128], target_key[128];
          snprintf(op_key, sizeof(op_key), "TargetOperationList.%d.Operation", i + 1);
          snprintf(target_key, sizeof(target_key), "TargetOperationList.%d.Target", i + 1);
    
          strncpy(pairs[pair_count].key, op_key, sizeof(pairs[pair_count].key));
          strncpy(pairs[pair_count].value, targetOperation_list[i].operation, sizeof(pairs[pair_count].value));
          pair_count++;
    
          strncpy(pairs[pair_count].key, target_key, sizeof(pairs[pair_count].key));
          strncpy(pairs[pair_count].value, targetOperation_list[i].target, sizeof(pairs[pair_count].value));
          pair_count++;
    }
        // Add the SelectionKey parameter.
        snprintf(pairs[pair_count].key, sizeof(pairs[pair_count].key), "SelectionKey");
        snprintf(pairs[pair_count].value, sizeof(pairs[pair_count].value), "%s", selection_key);
        pair_count++;
    
        // Sorting and encoding are both performed in get_authorization().
        qsort(pairs, pair_count, sizeof(KeyValuePair), compare_pairs);
    
        // Construct the original query string (unencoded).
        char query_params[BUFFER_SIZE] = {0};
        for (int i = 0; i < pair_count; ++i) {
          if (i == 0) {
            snprintf(query_params, sizeof(query_params), "%s=%s", pairs[i].key, pairs[i].value);
         } else {
            char temp[512];
            snprintf(temp, sizeof(temp), "&%s=%s", pairs[i].key, pairs[i].value);
            strncat(query_params, temp, sizeof(query_params) - strlen(query_params) - 1);
        }
    }
        const char *body = ""; // The request body is empty.
        const char *content_type = "application/json; charset=utf-8";
        call_api(http_method, canonical_uri, host, x_acs_action, x_acs_version, query_params, body, content_type, strlen(body));
    
        /**
          * RPC-style API request example: the request parameter is "in":"query".
        */
        // Define the API request parameters.
        // const char *http_method = "POST";
        // const char *canonical_uri = "/";
        // const char *host = "ecs.cn-hangzhou.aliyuncs.com";
        // const char *x_acs_action = "DescribeInstanceStatus";
        // const char *x_acs_version = "2014-05-26";
        // // Define the InstanceId array parameter. InstanceId is optional.
        // const char *instance_ids[] = {
        //     "i-bp11ht4hXXXXXXXX",
        //     "i-bp16maz3XXXXXXXX"
        // };
        // // Concatenate the InstanceId array.
        // char InstanceId[BUFFER_SIZE];
        // snprintf(InstanceId, sizeof(InstanceId),
        //          "InstanceId.1=%s&InstanceId.2=%s",
        //         instance_ids[0],
        //         instance_ids[1]);
        // // Define the query parameters. Required parameter: RegionId=cn-hangzhou    const char *query_params = "RegionId=cn-hangzhou";
        // char query_params[BUFFER_SIZE];
        // snprintf(query_params, sizeof(query_params),
        //          "%s&RegionId=cn-hangzhou", InstanceId);
        // const char *body = "";
        // const char *content_type = "application/json; charset=utf-8";
        // call_api(http_method, canonical_uri, host, x_acs_action, x_acs_version, query_params, body, content_type, strlen(body));
    
          /**
            * RPC-style API request example: the request parameter is "in":"body" (file upload scenario).
          */
        // Declare the pointer that stores the file content that is read.
        // char *body = NULL;
        // size_t body_length = read_file("<YOUR_FILE_PATH>", &body);
        // if (body_length > 0) {
        //   const char *http_method = "POST";
        //   const char *canonical_uri = "/";
        //   const char *host = "ocr-api.cn-hangzhou.aliyuncs.com";
        //   const char *x_acs_action = "RecognizeGeneral";
        //   const char *x_acs_version = "2021-07-07";
        //   const char *query_params = "";
        //   const char *content_type = "application/octet-stream";
        //   call_api(http_method, canonical_uri, host, x_acs_action, x_acs_version, query_params, body, content_type, body_length);
        //   free(body);
        // } else {
        //   fprintf(stderr, "File read error\n");
        // }
    
          /**
           * RPC-style API request example: the request parameter is "in": "formData" or "in":"body" (non-file upload scenario).
           */
        // const char *http_method = "POST";
        // const char *canonical_uri = "/";
        // const char *host = "mt.aliyuncs.com";
        // const char *x_acs_action = "TranslateGeneral";
        // const char *x_acs_version = "2018-10-12";
        // char query_params[BUFFER_SIZE];
        // snprintf(query_params, sizeof(query_params), "Context=%s", "Morning");
        // const char *format_type = "text";
        // const char *source_language = "zh";
        // const char *target_language = "en";
        // const char *source_text = "Hello";
        // const char *scene = "general";
        // char body[BUFFER_SIZE];
        // snprintf(body, sizeof(body),
        // "FormatType=%s&SourceLanguage=%s&TargetLanguage=%s&SourceText=%s&Scene=%s",
        // percentEncode(format_type), percentEncode(source_language), percentEncode(target_language),
        // percentEncode(source_text), percentEncode(scene));
        // const char *content_type = "application/x-www-form-urlencoded";
        // printf("formdate_body: %s\n", body);
        // call_api(http_method, canonical_uri, host, x_acs_action, x_acs_version, query_params, body, content_type, strlen(body));
    
       // RPC-style API request example 3: the request parameter is "in": "formData".
    //    const char *http_method = "POST";
    //    const char *canonical_uri = "/";
    //    const char *host = "sasti.aliyuncs.com";
    //    const char *x_acs_action = "AskTextToTextMsg";
    //    const char *x_acs_version = "2020-05-12";
    //    // query
    //    const char *query_params = "";
    //    // body
    //    const char *Memory = "false";
    //    const char *Stream = "true";
    //    const char *ProductCode = "sddp_pre";
    //    const char *Feature = "{}";
    //    const char *Model = "yunsec-llm-latest";
    //    const char *Type = "Chat";
    //    const char *TopP = "0.9";
    //    const char *Temperature = "0.01";
    //    const char *Prompt = "Who are you";
    //    const char *Application = "sddp_pre";
    //    char body[BUFFER_SIZE];
    //    snprintf(body, sizeof(body),
    //            "Memory=%s&Stream=%s&ProductCode=%s&Feature=%s&Model=%s&Type=%s&TopP=%s&Temperature=%s&Prompt=%s&Application=%s",
    //            Memory, Stream, ProductCode, Feature, Model, Type, TopP, Temperature, Prompt, Application);
    //    const char *content_type = "application/x-www-form-urlencoded";
    //    printf("formdate_body: %s\n", body);
    //    call_api(http_method, canonical_uri, host, x_acs_action, x_acs_version, query_params, body, content_type, strlen(body));
    
          /**
            * ROA-style API POST request with "in" "body".
          */
    //    const char *http_method = "POST";
    //    const char *canonical_uri = "/clusters";
    //    const char *host = "cs.cn-beijing.aliyuncs.com";
    //    const char *x_acs_action = "CreateCluster";
    //    const char *x_acs_version = "2015-12-15";
    //    const char *query_params = "";
    //    char body[BUFFER_SIZE];
    //    snprintf(body, sizeof(body),
    //             "{\"name\":\"%s\",\"region_id\":\"%s\",\"cluster_type\":\"%s\","
    //             "\"vpcid\":\"%s\",\"container_cidr\":\"%s\","
    //             "\"service_cidr\":\"%s\",\"security_group_id\":\"%s\","
    //             "\"vswitch_ids\":[\"%s\"]}",
    //             "test-cluster", "cn-beijing", "ExternalKubernetes",
    //             "vpc-2zeou1uod4yXXXXXXXX", "10.X.X.X/XX",
    //             "10.X.X.X/XX", "sg-2ze1a0rlgeXXXXXXXX",
    //             "vsw-2zei30dhflXXXXXXXX");
    //    const char *content_type = "application/json; charset=utf-8";
    //    call_api(http_method, canonical_uri, host, x_acs_action, x_acs_version, query_params, body, content_type, strlen(body));
    
          /**
            * ROA-style API GET request.
          */
    //    const char *http_method = "GET";
    //    char canonical_uri[BUFFER_SIZE];
    //    snprintf(canonical_uri, sizeof(canonical_uri), "/clusters/%s/resources", percentEncode("cd1f5ba0dbfa144XXXXXXXX"));
    //    const char *host = "cs.cn-beijing.aliyuncs.com";
    //    const char *x_acs_action = "DescribeClusterResources";
    //    const char *x_acs_version = "2015-12-15";
    //    const char *query_params = "with_addon_resources=true";
    //    const char *body = "";
    //    const char *content_type = "";
    //    call_api(http_method, canonical_uri, host, x_acs_action, x_acs_version, query_params, body, content_type, strlen(body));
    
          /**
            *  ROA-style API DELETE request.
          */
    //    const char *http_method = "DELETE";
    //    char canonical_uri[BUFFER_SIZE];
    //    snprintf(canonical_uri, sizeof(canonical_uri), "/clusters/%s", percentEncode("cd1f5ba0dbfa144XXXXXXXX"));
    //    const char *host = "cs.cn-beijing.aliyuncs.com";
    //    const char *x_acs_action = "DeleteCluster";
    //    const char *x_acs_version = "2015-12-15";
    //    const char *query_params = "";
    //    const char *body = "";
    //    const char *content_type = "";
    //    call_api(http_method, canonical_uri, host, x_acs_action, x_acs_version, query_params, body, content_type, strlen(body));
    
        // The variables that store the generated values.
        char authorization_header[BUFFER_SIZE];
        char hashed_payload[SHA256_DIGEST_LENGTH * 2 + 1];
        char x_acs_date[64];
        char uuid[37];
        return 0;
    }

Perguntas frequentes

Falha na assinatura com retorno da mensagem de erro "Specified signature does not match our calculation." ou "The request signature does not conform to Aliyun standards."

Causas:

A maioria das falhas de assinatura ocorre durante a construção do CanonicalRequest. As causas mais comuns são:

  • O AccessKey ID ou o AccessKey secret está configurado incorretamente, ou o par de AccessKeys foi desativado ou excluído.

  • Um parâmetro foi passado na posição errada. Por exemplo, um parâmetro de consulta foi enviado no corpo da requisição.

  • Os parâmetros em CanonicalQueryString não estão ordenados em ordem crescente.

  • Os cabeçalhos de requisição em CanonicalHeaders não estão classificados em ordem alfabética minúscula. A assinatura V3 exige que os nomes dos cabeçalhos sigam rigorosamente a ordem alfabética. Por exemplo, content-type vem antes de host, e host vem antes de x-acs-action.

  • O HashedRequestPayload foi calculado incorretamente. Para uma requisição POST cujo corpo é um objeto JSON vazio {}, o valor de hash SHA-256 é 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a. Não utilize o valor de hash de uma string vazia.

  • Espaços não foram codificados como %20.

  • A codificação URL foi aplicada uma vez a mais. Durante o cálculo da assinatura, a codificação URL é necessária apenas uma vez, ao processar parâmetros de caminho e a string de consulta canônica. Por exemplo, uma grande quantidade de caracteres %25 em uma mensagem de erro indica que o caractere % foi codificado indevidamente.

  • A chave usada para a assinatura HMAC-SHA256 está incorreta. A chave de assinatura deve ser o AccessKey secret original. Não aplique codificação Base64 nem qualquer outra codificação.Soluções:

Verifique se o resultado do seu cálculo local corresponde ao resultado esperado descrito em e, em seguida, valide os itens abaixo:

  • Confirme se o AccessKey ID e o AccessKey secret estão corretos e se o par de AccessKeys está ativado. Visualize o status do par na página de gerenciamento de AccessKeys no console do Resource Access Management (RAM).

  • Compare o CanonicalRequest presente na mensagem de erro com aquele que você calculou localmente. A mensagem de erro retornada pelo servidor contém o StringToSign e o CanonicalRequest esperados. Faça uma comparação linha por linha com seu cálculo local para identificar a diferença. Caso haja divergência, revise as causas comuns listadas acima e a descrição em , verificando seu código com atenção.

  • Se o CanonicalRequest for idêntico, verifique se o StringToSign da mensagem de erro difere do StringToSign calculado localmente. O formato do StringToSign é ACS3-HMAC-SHA256 seguido por uma quebra de linha e o valor de hash SHA-256 do CanonicalRequest. Se houver diferença, o algoritmo de hash pode estar incorreto.

  • Quando o StringToSign também for igual, duas causas são possíveis: o AccessKey secret está errado ou o formato da chave usada no cálculo HMAC-SHA256 está inadequado. A chave deve ser obrigatoriamente o AccessKey secret original.

  • Caso o problema persista, consulte .

Como usar o Postman para testes?

Não é possível chamar operações de API da Alibaba Cloud diretamente pelo Postman. Para testar operações de API com essa ferramenta, siga estes passos:

  • Utilize código ou script para calcular o cabeçalho Authorization conforme o método de assinatura.

  • Copie os cabeçalhos de requisição do CanonicalHeaders para Headers no Postman e adicione as informações de Authorization em Headers. Exemplo:

Chave

Valor de exemplo

host

dysmsapi.aliyuncs.com

x-acs-action

SendSms

x-acs-content-sha256

e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

x-acs-date

2025-04-16T07:45:55Z

x-acs-signature-nonce

315484d3-b129-4966-974a-699b7ee56647

x-acs-version

2017-05-25

Authorization

ACS3-HMAC-SHA256 Credential=testAccessKeyId,SignedHeaders=host;x-acs-action;x-acs-content-sha256;x-acs-date;x-acs-signature-nonce;x-acs-version,Signature=b37aac99faa507472778256374366b7a47ba48adbc484a53ad789db194658a2d

  • Configure os parâmetros no Postman de acordo com o tipo de cada um. A ordem dos parâmetros deve ser idêntica àquela utilizada no cálculo da assinatura.

    • Para parâmetros de consulta, insira-os em Params.

    • Para parâmetros de corpo, insira-os em Body.

Como obter a versão da API (x-acs-version)?

  • Acesse o Portal do Desenvolvedor OpenAPI da Alibaba Cloud e selecione o service da Alibaba Cloud correspondente à operação de API desejada. Este exemplo utiliza o ECS. Na barra de navegação superior do OpenAPI Explorer, clique em Select a product e vá para a página inicial do service a fim de visualizar a versão recomendada da API.

  • Visualize a versão recomendada da API na página inicial do service da Alibaba Cloud. Por exemplo, a versão recomendada para o ECS é 2014-05-26. As abas Service regions e API overview estão disponíveis na parte inferior da página. Utilize a lista suspensa de versões da API para alternar para a versão correspondente e consultar os detalhes.

Retorno da mensagem de erro "You are not authorized to do this operation." ao chamar uma operação de API

Causa: O usuário do Resource Access Management (RAM) associado ao par de AccessKeys utilizado não possui permissões para chamar a operação de API.

Solução: Consulte code 403, You are not authorized to do this operation. Action: xxxx..

Como obter um par de AccessKeys?

Um par de AccessKeys é uma credencial de acesso permanente fornecida pela Alibaba Cloud ao usuário, composta por um AccessKey ID e um AccessKey secret. Ao acessar recursos da Alibaba Cloud via chamadas de API, o sistema realiza a verificação de identidade e valida a requisição com base no AccessKey ID transmitido e na assinatura gerada com o AccessKey secret. Para mais informações sobre como obter um par de AccessKeys, consulte Create an AccessKey pair for a RAM user.

Fale conosco

Se você encontrar um problema relacionado ao cálculo de assinatura que não consiga resolver, entre no grupo do DingTalk 147535001692 e contate o engenheiro de plantão.

Não entre neste grupo para questões não relacionadas ao cálculo de assinatura, pois você pode não receber uma resposta válida.