Assinaturas das Solicitações

Este tópico descreve como assinar solicitações da API do Oracle Cloud Infrastructure.

São incluídos exemplos de assinatura para o seguinte:

Versão 1 da Assinatura

A assinatura descrita aqui é a versão 1 da assinatura da API do Oracle Cloud Infrastructure. No futuro, se a Oracle modificar o método para assinar solicitações, o número da versão será incrementado, e a sua empresa será notificada.

Credenciais e OCIDs Necessários

Você precisa de uma chave de assinatura de API no formato correto. Consulte Chaves e OCIDs Necessários.

Cuidado

Diferença de Clock do Cliente

Se o clock do cliente estiver desalinhado em mais de 5 minutos, um código de status HTTP 401 (NotAuthenticated) será retornado. Isso afetará as suas solicitações da API. Para obter mais informações, consulte Diferença Máxima Permitida para o Clock do Cliente.

Você também precisa dos OCIDs para a sua tenancy e o seu usuário. Consulte Onde Obter o OCID da Tenancy e o OCID do Usuário.

Resumo das Etapas de Assinatura

Em geral, estas são as etapas necessárias para assinar uma solicitação:

  1. Forme a solicitação HTTPS (o protocolo SSL TLS 1.2 é necessário).
  2. Crie a string de assinatura, que se baseia em partes da solicitação.
  3. Crie a assinatura com base na string de assinatura, usando a sua chave privada e o algoritmo RSA-SHA256.
  4. Adicione a assinatura resultante e outras informações necessárias ao cabeçalho Authorization da solicitação.

Consulte as próximas seções desse tópico para obter detalhes sobre essas etapas.

Especificação com Que Você Precisa Estar Familiarizado

Para saber como executar as etapas 2-4 no processo acima, consulte draft-cavage-http-signatures-08. É uma especificação preliminar que forma a base de como a Oracle trata as assinaturas das solicitações. Essa especificação descreve geralmente como formar a string de assinatura, como criar a assinatura e como adicionar a assinatura e as informações necessárias à solicitação. As próximas seções desse tópico supõem que você esteja familiarizado com a string de assinatura. Detalhes importantes da implementação do Oracle Cloud Infrastructure para a referência são listados na seção a seguir.

Detalhes Especiais da Implementação

As seções a seguir descrevem itens importantes a serem observados sobre a implementação do Oracle Cloud Infrastructure para a especificação.

Cabeçalho de Autorização

A assinatura do Oracle Cloud Infrastructure usa o esquema de Autenticação Signature (com um cabeçalho Authorization), e não o cabeçalho HTTP de Assinatura.

Cabeçalhos Obrigatórios

Esta seção descreve os cabeçalhos que devem ser incluídos na string de assinatura.

Observação

Erro se o Cabeçalho Necessário Estiver Ausente

Se o cabeçalho necessário não for encontrado, o seu cliente receberá uma resposta 401 "Não Autorizado".

Para solicitações GET e DELETE (quando não há conteúdo no corpo da solicitação), a string de assinatura deverá incluir pelo menos estes cabeçalhos:

  • (request-target) (conforme descrito em draft-cavage-http-signatures-08)
  • host
  • date ou x-date (se ambos estiverem incluídos, o sistema Oracle usará x-date)

Para solicitações PUT e POST (quando há conteúdo no corpo da solicitação), a string de assinatura deverá incluir pelo menos estes cabeçalhos:

  • (request-target)
  • host
  • date ou x-date (se ambos estiverem incluídos, o sistema Oracle usará x-date)
  • x-content-sha256 (exceto para solicitações PUT do serviço Object Storage; consulte a seção a seguir)
  • content-type
  • content-length
Cuidado

Para solicitações PUT e POST, o seu cliente deve computar o parâmetro x-content-sha256 e incluí-lo na string de assinatura e solicitação, mesmo que o corpo seja uma string vazia. Além disso, o parâmetro content-length sempre é necessário na string de assinatura e solicitação, mesmo que o corpo esteja vazio. Alguns clientes HTTP não enviarão o parâmetro content-length se o corpo estiver vazio; portanto, você deverá garantir explicitamente que o seu cliente o envie. Se date e x-date estiverem incluídos, o sistema Oracle usará x-date. O parâmetro x-date é usado para proteção contra a reutilização da parte assinada da solicitação (ataques de repetição).

A única exceção é para solicitações PUT do serviço Object Storage em objetos (consulte a seção a seguir).

Instruções Especiais para PUT no Serviço Object Storage

Para solicitações PUT PutObject e UploadPart no serviço Object Storage, a string de assinatura deve incluir pelo menos estes cabeçalhos:

  • (request-target)
  • host
  • date ou x-date (se ambos estiverem incluídos, o sistema Oracle usará x-date)

Se a solicitação também incluir qualquer um dos outros cabeçalhos que normalmente são necessários para solicitações PUT (consulte a lista acima), esses cabeçalhos também deverão ser incluídos na string de assinatura.

Uso de Letras Maiúsculas e Minúsculas e Ordem dos Cabeçalhos

Os cabeçalhos devem estar todos em minúsculas na string de assinatura.

A ordem dos cabeçalhos na string de assinatura não importa. Apenas certifique-se de especificar a ordem no parâmetro headers no cabeçalho Authorization, conforme descrito em draft-cavage-http-signatures-05.

Cuidado

O (request-target) inclui o caminho e a string de consulta da solicitação. O sistema Oracle espera que você crie a string de assinatura com os parâmetros de consulta na mesma ordem que aparecem na solicitação. Se a ordem dos parâmetros de consulta de solicitação mudar após a assinatura ocorrer, a autenticação falhará.

Codificação do URL do Caminho e da String de Consulta

Ao formar a string de assinatura, você deverá codificar todos os parâmetros no caminho e na string de consulta (mas não os cabeçalhos) de acordo com a RFC 3986.

Identificador de Chave

Defina keyId="<TENANCY OCID>/<USER OCID>/<KEY FINGERPRINT>" no cabeçalho Authorization adicionado à solicitação. Para obter esses valores, consulte Onde Obter o OCID da Tenancy e o OCID do Usuário. Um exemplo de keyId se parece com (distribuído de forma a se ajustar melhor à página):

ocid1.tenancy.oc1..<unique_ID>/ocid1.user.oc1..<unique_ID>/<key_fingerprint>

Algoritmo de Assinatura

O algoritmo de assinatura deve ser RSA-SHA256, e você deverá definir algorithm="rsa-sha256" no cabeçalho Authorization (observe as aspas).

Versão da Assinatura

Você deverá incluir version="1" no cabeçalho Authorization (observe as aspas). Se não incluir, será pressuposto que você está usando a versão atual (que desta vez é a versão 1).

Exemplo de Cabeçalho

Este é um exemplo da sintaxe geral do cabeçalho Authorization (para uma solicitação com conteúdo no corpo):

Authorization: Signature version="1",keyId="<tenancy_ocid>/<user_ocid>/<key_fingerprint>",algorithm="rsa-sha256",headers="(request-target) date x-content-sha256 content-type content-length",signature="Base64(RSA-SHA256(<signing_string>))"

Valores de Teste

Este é um exemplo de par de chaves, dois exemplos de solicitação e o cabeçalho Authorization resultante para cada um deles.

Cuidado

As assinaturas de exemplo usam as chaves RSA de 2048 bits a seguir. Use essas chaves somente para testar o seu código de assinatura, e não para enviar solicitações de produção.

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCFENGw33yGihy92pDjZQhl0C3
6rPJj+CvfSC8+q28hxA161QFNUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6
Z4UMR7EOcpfdUE9Hf3m/hs+FUR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJw
oYi+1hqp1fIekaxsyQIDAQAB
-----END PUBLIC KEY-----
						
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDCFENGw33yGihy92pDjZQhl0C36rPJj+CvfSC8+q28hxA161QF
NUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6Z4UMR7EOcpfdUE9Hf3m/hs+F
UR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJwoYi+1hqp1fIekaxsyQIDAQAB
AoGBAJR8ZkCUvx5kzv+utdl7T5MnordT1TvoXXJGXK7ZZ+UuvMNUCdN2QPc4sBiA
QWvLw1cSKt5DsKZ8UETpYPy8pPYnnDEz2dDYiaew9+xEpubyeW2oH4Zx71wqBtOK
kqwrXa/pzdpiucRRjk6vE6YY7EBBs/g7uanVpGibOVAEsqH1AkEA7DkjVH28WDUg
f1nqvfn2Kj6CT7nIcE3jGJsZZ7zlZmBmHFDONMLUrXR/Zm3pR5m0tCmBqa5RK95u
412jt1dPIwJBANJT3v8pnkth48bQo/fKel6uEYyboRtA5/uHuHkZ6FQF7OUkGogc
mSJluOdc5t6hI1VsLn0QZEjQZMEOWr+wKSMCQQCC4kXJEsHAve77oP6HtG/IiEn7
kpyUXRNvFsDE0czpJJBvL/aRFUJxuRK91jhjC68sA7NsKMGg5OXb5I5Jj36xAkEA
gIT7aFOYBFwGgQAQkWNKLvySgKbAZRTeLBacpHMuQdl1DfdntvAyqpAZ0lY0RKmW
G6aFKaqQfOXKCyWoUiVknQJAXrlgySFci/2ueKlIE1QqIiLSZ8V8OlpFLRnb1pzI
7U1yQXnTAEFYM560yJlzUpOb1V4cScGd365tiSMvxLOvTA==
-----END RSA PRIVATE KEY-----


The public key is stored under keyId:

ocid1.tenancy.oc1..<unique_ID>/ocid1.user.oc1..<unique_ID>/<key_fingerprint>



For the following GET request (line breaks inserted between query parameters for easier reading; also notice the URL encoding as mentioned earlier):
			
GET https://iaas.us-phoenix-1.oraclecloud.com/20160918/instances
?availabilityDomain=Pjwf%3A%20PHX-AD-1
&compartmentId=ocid1.compartment.oc1...<unique_ID>
&displayName=TeamXInstances
&volumeId=ocid1.volume.oc1.phx.<unique_ID>
Date: Thu, 05 Jan 2014 21:31:40 GMT
			
The signing string would be (line breaks inserted into the (request-target) header for easier reading):
			
date: Thu, 05 Jan 2014 21:31:40 GMT
(request-target): get /20160918/instances?availabilityDomain=Pjwf%3A%20PH
X-AD-1&compartmentId=ocid1.compartment.oc1..aaaaaaaam3we6vgnherjq5q2i
dnccdflvjsnog7mlr6rtdb25gilchfeyjxa&displayName=TeamXInstances&
volumeId=ocid1.volume.oc1.phx.abyhqljrgvttnlx73nmrwfaux7kcvzfs3s66izvxf2h
4lgvyndsdsnoiwr5q
host: iaas.us-phoenix-1.oraclecloud.com

The Authorization header would be:
Signature version="1",headers="date (request-target) host",keyId="ocid1.t
enancy.oc1..<unique_ID>/ocid1.user.oc1..<unique_ID>/<key_fingerprint>,algorithm="rsa-sha256
",signature="<your_signature>"

For the following POST request:
			
POST https://iaas.us-phoenix-1.oraclecloud.com/20160918/volumeAttachments
Date: Thu, 05 Jan 2014 21:31:40 GMT
{
   "compartmentId": "ocid1.compartment.oc1..<unique_id>",
   "instanceId": "ocid1.instance.oc1.phx.<unique_id>",
   "volumeId": "ocid1.volume.oc1.phx.<unique_id>"
}

The signing string would be:
			
date: Thu, 05 Jan 2014 21:31:40 GMT
(request-target): post /20160918/volumeAttachments
host: iaas.us-phoenix-1.oraclecloud.com
content-length: 316
content-type: application/json
x-content-sha256: V9Z20UJTvkvpJ50flBzKE32+6m2zJjweHpDMX/U4Uy0=
			
The Authorization header would be:	

Signature version="1",headers="date (request-target) host content-length c
ontent-type x-content-sha256",
keyId="ocid1.tenancy.oc1..<unique_id>/ocid1.user.oc1.<unique_id>/<your_fingerprint>",
algorithm="rsa-sha256",signature="<your_signature>"

Exemplo de Código

Esta seção mostra o código básico para assinar solicitações da API.

Java

/**
* Copyright (c) 2016, 2020, Oracle and/or its affiliates.  All rights reserved.
* This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl 
* or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.
*/
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;

import com.google.common.net.UrlEscapers;

import com.oracle.bmc.http.signing.RequestSigningFilter;

public class RawRestCallExample {

public static void main(String[] args) throws Exception {
// TODO: fill this out
String instanceId = null;

String configurationFilePath = "~/.oci/config";
String profile = "DEFAULT";

// Pre-Requirement: Allow setting of restricted headers. This is required to allow the SigningFilter
// to set the host header that gets computed during signing of the request.
System.setProperty("sun.net.http.allowRestrictedHeaders", "true");

// 1) Create a request signing filter instance
RequestSigningFilter requestSigningFilter =
RequestSigningFilter.fromConfigFile(configurationFilePath, profile);

// 2) Create a Jersey client and register the request signing filter
Client client = ClientBuilder.newBuilder().build().register(requestSigningFilter);

// 3) Target an endpoint. You must ensure that path arguments and query
// params are escaped correctly yourself
WebTarget target =
client.target("https://iaas.us-phoenix-1.oraclecloud.com")
.path("20160918")
.path("instances")
.path(UrlEscapers.urlPathSegmentEscaper().escape(instanceId));

// 4) Set the expected type and invoke the call
Invocation.Builder ib = target.request();
ib.accept(MediaType.APPLICATION_JSON);
Response response = ib.get();

// 5) Print the response headers and the body (JSON) as a string
MultivaluedMap<String, Object> responseHeaders = response.getHeaders();
System.out.println(responseHeaders);
InputStream responseBody = (InputStream) response.getEntity();
try (final BufferedReader reader =
new BufferedReader(new InputStreamReader(responseBody, StandardCharsets.UTF_8))) {
StringBuilder jsonBody = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
jsonBody.append(line);
}
System.out.println(jsonBody.toString());
}
}
		}

Python

Importante

Esse exemplo de código Python requer TLS 1.2, que não está incluído no Python padrão do Mac OS X.
# coding: utf-8
# Copyright (c) 2016, 2020, Oracle and/or its affiliates.  All rights reserved.
# This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl 
# or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.

import requests
from oci.config import from_file
from oci.signer import Signer

config = from_file()
auth = Signer(
tenancy=config['tenancy'],
user=config['user'],
fingerprint=config['fingerprint'],
private_key_file_location=config['key_file'],
pass_phrase=config['pass_phrase']
)

endpoint = 'https://identity.us-phoenix-1.oraclecloud.com/20160918/users/'

body = {
'compartmentId': config['tenancy'],  # root compartment
'name': 'TestUser',
'description': 'Created with a raw request'
}

response = requests.post(endpoint, json=body, auth=auth)
response.raise_for_status()

print(response.json()['id'])

TypeScript

/**
 * Copyright (c) 2020, Oracle and/or its affiliates.  All rights reserved.
 * This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl 
 * or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.
 */

import { DefaultRequestSigner, HttpRequest } from "oci-common";
import { provider } from "./authentication";
import * as promise from "es6-promise";
import "isomorphic-fetch";
promise.polyfill();

const userID = "Add User OCID here";
(async () => {
  // 1. Create Request Signing instance
  const signer = new DefaultRequestSigner(provider);

  // 2. Create HttpRequest to be signed
  const httpRequest: HttpRequest = {
    uri: `https://identity.us-phoenix-1.oraclecloud.com/20160918/users/${userID}`,
    headers: new Headers(),
    method: "GET"
  };

  // 3. sign request
  await signer.signHttpRequest(httpRequest);

  // 4. Make the call
  const response = await fetch(
    new Request(httpRequest.uri, {
      method: httpRequest.method,
      headers: httpRequest.headers,
      body: httpRequest.body
    })
  );
  // 5. Print response
  console.log(await response.json());
})();

JavaScript

/**
 * Copyright (c) 2020, Oracle and/or its affiliates.  All rights reserved.
 * This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl 
 * or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.
 */

const common = require("oci-common");
const promise = require("es6-promise");
require("isomorphic-fetch");
promise.polyfill();

const configurationFilePath = "~/.oci/config";
const configProfile = "DEFAULT";

const provider = new common.ConfigFileAuthenticationDetailsProvider(
  configurationFilePath,
  configProfile
);

const userID = "<INSERT_SAMPLE_USER_OCID_HERE>";
(async () => {
  // 1. Create Request Signing instance
  const signer = new common.DefaultRequestSigner(provider);

  // 2. Create HttpRequest to be signed
  const httpRequest = {
    uri: `https://identity.us-phoenix-1.oraclecloud.com/20160918/users/${userID}`,
    headers: new Headers(),
    method: "GET"
  };

  // 3. sign request
  await signer.signHttpRequest(httpRequest);

  // 4. Make the call
  const response = await fetch(
    new Request(httpRequest.uri, {
      method: httpRequest.method,
      headers: httpRequest.headers,
      body: httpRequest.body
    })
  );
  // 5. Print response
  console.log(await response.json());
})();

Ruby


# Copyright (c) 2016, 2020, Oracle and/or its affiliates.  All rights reserved.
# This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or 
# Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.

require 'oci'
require 'net/http'

config = OCI::ConfigFileLoader.load_config(config_file_location:my_config_file_location)
endpoint = OCI::Regions.get_service_endpoint(config.region, :IdentityClient)

uri = URI(endpoint + '/20160918/users/' + config.user)
request = Net::HTTP::Get.new(uri)

signer = OCI::Signer.new(config.user, config.fingerprint, config.tenancy, config.key_file, pass_phrase:my_private_key_pass_phrase)
signer.sign(:get, uri.to_s, request, nil)

result = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => true) {|http|
http.request(request)
}

puts result.body

Go

O exemplo a seguir mostra como criar um signatário padrão.

Observação

O SDK para Go expõe um signatário independente que você pode usar para assinar solicitações personalizadas. Você pode encontrar um código relacionado em http_signer.go.
// Copyright (c) 2016, 2018, 2020, Oracle and/or its affiliates.  All rights reserved.
// This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or 
// Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.

// Example code for sending raw request to  Service API

package example

import (
"fmt"
"io/ioutil"
"log"
"net/http"
"time"

"github.com/oracle/oci-go-sdk/common"
"github.com/oracle/oci-go-sdk/example/helpers"
)

// ExampleRawRequest compose a request, sign it and send to server
func ExampleListUsers_RawRequest() {
// build the url
url := "https://identity.us-phoenix-1.oraclecloud.com/20160918/users/?compartmentId=" + *helpers.RootCompartmentID()

// create request
request, err := http.NewRequest("GET", url, nil)
helpers.FatalIfError(err)

// Set the Date header
request.Header.Set("Date", time.Now().UTC().Format(http.TimeFormat))

// And a provider of cryptographic keys
provider := common.DefaultConfigProvider()

// Build the signer
signer := common.DefaultRequestSigner(provider)

// Sign the request
signer.Sign(request)

client := http.Client{}

fmt.Println("send request")

// Execute the request
resp, err := client.Do(request)
helpers.FatalIfError(err)

defer resp.Body.Close()

log.Println("response Status:", resp.Status)
log.Println("response Headers:", resp.Header)

body, _ := ioutil.ReadAll(resp.Body)
log.Println("response Body:", string(body))

fmt.Println("receive response")

// Output:
// send request
// receive response
}

Bash

Exiba o exemplo com Bash em tela cheia para facilitar a leitura.



#!/bin/bash
# Copyright (c) 2016, 2020, Oracle and/or its affiliates.  All rights reserved.
# This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.

set -e

if [[ -z "$COMPARTMENT_ID" ]];then
    echo "COMPARTMENT_ID must be defined in the environment. "
    exit 1
fi

USER_NAME="TestUser"
USER_DESCRIPTION="User created by raw request"
TARGET_URI='https://identity.us-phoenix-1.oraclecloud.com/20160918/users/'
HTTP_METHOD='POST'
PROFILE='ADMIN'
REQUEST_BODY="{\"compartmentId\": \"$COMPARTMENT_ID\", \"name\": \"$USER_NAME\", \"description\": \"$USER_DESCRIPTION\"}"


echo "oci raw-request --profile ${PROFILE} --target-uri ${TARGET_URI} --http-method ${HTTP_METHOD} --request-body "${REQUEST_BODY}" | jq -r '.data.id'"
USER_OCID=$(oci raw-request --profile ${PROFILE} --target-uri ${TARGET_URI} --http-method ${HTTP_METHOD} --request-body "${REQUEST_BODY}" | jq -r '.data.id')

echo "Created user OCID: $USER_OCID"

C#


/*
 * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
 * This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or 
 * Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.
 */

using System;
using System.Net.Http;
using System.Threading.Tasks;
using Oci.Common.Http.Signing;

namespace Oci.Examples
{
    public class RawRestCallExample
    {
        private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

        public static async Task MainRaw()
        {
            var namespaceName = Environment.GetEnvironmentVariable("NAMESPACE_NAME");
            var compartmentId = Environment.GetEnvironmentVariable("COMPARTMENT_ID");

            var httpClientHandler = OciHttpClientHandler.FromConfigFile("~/.oci/config", "DEFAULT");
            var GET_BUCKETS_URL = $"https://objectstorage.us-phoenix-1.oraclecloud.com/n/{namespaceName}/b/?compartmentId={compartmentId}";
            var client = new HttpClient(httpClientHandler);
            var requestMessage = new HttpRequestMessage(HttpMethod.Get, new Uri(GET_BUCKETS_URL));

            var response = await client.SendAsync(requestMessage);

            logger.Info($"Is rest call successful: {response.IsSuccessStatusCode}");
            var responseJson = await response.Content.ReadAsStringAsync();
            logger.Info($"Parsed Response: {responseJson}");
        }
    }
}