Firmas de solicitud

En este tema se describe cómo firmar solicitudes de API de Oracle Cloud Infrastructure.

Se incluyen ejemplos de firma para lo siguiente:

Versión de firma 1

La firma que se describe aquí es la versión 1 de la firma de API de Oracle Cloud Infrastructure. En el futuro, si Oracle modifica el método para firmar solicitudes, el número de versión se incrementará y se le notificará a su compañía.

Credenciales y OCID necesarios

Necesita una clave de firma de API con el formato correcto. Consulte Claves y OCID necesarios.

Atención

Sesgo de reloj del cliente

Si el reloj del cliente está sesgado más de 5 minutos, se devuelve un código de estado de HTTP 401 (NotAuthenticated). Esto afectará a sus solicitudes de API. Para obtener más información, consulte Máximo sesgo de reloj del cliente permitido.

También necesita los OCID para su arrendamiento y usuario. Consulte Dónde obtener el OCID del arrendamiento y el OCID del usuario.

Resumen de pasos de la firma

En general, estos son los pasos necesarios para firmar una solicitud:

  1. Haga la solicitud HTTPS (se necesita el protocolo SSL TLS 1.2).
  2. Cree la cadena de firma, que se basa en partes de la solicitud.
  3. Cree la firma a partir de la cadena de firma, utilizando su clave privada y el algoritmo RSA- SHA256.
  4. Agregue la firma resultante y otra información necesaria a la cabecera Authorization de la solicitud.

Consulte las secciones restantes de este tema para obtener detalles sobre estos pasos.

Especificación con la que debe estar familiarizado

Para obtener más información sobre cómo realizar los pasos del 2 al 4 en el proceso anterior, consulte draft-cavage-http-signatures-08. Es una especificación de borrador que forma la base para el modo en que Oracle maneja las firmas de solicitud. En general, describe cómo formar la cadena de firma, cómo crear la firma y cómo agregar la firma e información necesaria a la solicitud. En las demás secciones de este tema se presupone que está familiarizado con él. En la siguiente sección, se enumeran detalles importantes sobre la implantación de la referencia por parte de Oracle Cloud Infrastructure.

Detalles de implantación especiales

En las siguientes secciones se describen elementos importantes para tener en cuenta acerca de la implantación de la especificación por parte de Oracle Cloud Infrastructure.

Cabecera de autorización

La firma de Oracle Cloud Infrastructure utiliza el esquema de autenticación Firma (con una cabecera Authorization) y no la cabecera HTTP de firma.

Cabeceras necesarias

En esta sección se describen las cabeceras que se deben incluir en la cadena de firma.

Nota

Error si falta la cabecera necesaria

Si falta una cabecera necesaria, el cliente recibirá una respuesta 401 "No autorizado".

Para las solicitudes GET y DELETE (cuando no hay contenido en el cuerpo de la solicitud), la cadena de firma debe incluir al menos estas cabeceras:

Para las solicitudes PUT y POST (cuando hay contenido en el cuerpo de la solicitud), la cadena de firma debe incluir al menos estas cabeceras:

  • (request-target)
  • host
  • date o x-date (si se incluyen ambos, Oracle utiliza x-date)
  • x-content-sha256 (excepto las solicitudes PUT de Object Storage; consulte la sección siguiente)
  • content-type
  • content-length
Atención

Para las solicitudes PUT y POST, su cliente debe calcular x-content-sha256 e incluirlo en la cadena de solicitud y firma, incluso si el cuerpo es una cadena vacía. Además, siempre se necesita content-length en la cadena de solicitud y firma, incluso si el cuerpo está vacío. Algunos clientes HTTP no enviarán el elemento content-length si el cuerpo está vacío, por lo que debe asegurarse explícitamente de que el cliente lo envía. Si se incluyen date y x-date, Oracle utiliza x-date. x-date se utiliza para la protección contra la reutilización de la parte firmada de la solicitud (ataques de reproducción).

La única excepción es para las solicitudes PUT de Object Storage en los objetos (consulte la sección siguiente).

Instrucciones especiales para solicitudes PUT de Object Storage

Para las solicitudes PUT PutObject y UploadPart de Object Storage, la cadena de firma debe incluir al menos estas cabeceras:

  • (request-target)
  • host
  • date o x-date (si se incluyen ambos, Oracle utiliza x-date)

Si la solicitud también incluye alguna de las otras cabeceras que normalmente se necesitan para las solicitudes PUT (consulte la lista anterior), dichas cabeceras también se deben incluir en la cadena de firma.

Mayúsculas/minúsculas y orden de las cabeceras

Todas las cabeceras deben estar en minúscula en la cadena de firma.

El orden de las cabeceras en la cadena de firma no importa. Asegúrese de especificar el orden en el parámetro headers en la cabecera Authorization, como se describe en draft-cavage-http-signatures-05.

Atención

(request-target) incluye la ruta de acceso y la cadena de consulta de la solicitud. Oracle espera que cree la cadena de firma con los parámetros de consulta en el mismo orden en que aparecen en la solicitud. Si los parámetros de consulta de la solicitud cambian el orden después de la firma, la autenticación fallará.

Codificación de URL de la ruta de acceso y la cadena de consulta

Al formar la cadena de firma, debe codificar la URL de todos los parámetros de la ruta de acceso y la cadena de consulta (pero no las cabeceras) según RFC 3986.

Identificador de clave

Debe definir keyId="<TENANCY OCID>/<USER OCID>/<KEY FINGERPRINT>" en la cabecera Authorization que agregue a la solicitud. Para obtener esos valores, consulte Dónde obtener el OCID del arrendamiento y el OCID del usuario. Un keyId de ejemplo es similar a esto (ajustado para adaptarse mejor a la página):

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

Algoritmo de firma

El algoritmo de firma debe ser RSA- SHA256 y debe definir algorithm="rsa-sha256" en la cabecera Authorization (tenga en cuenta las comillas).

Versión de la firma

Debe incluir version="1" en la cabecera Authorization (tenga en cuenta las comillas). Si no es así, se entiende que va a utilizar cualquiera que sea la versión actual (que es la versión 1 en este momento).

Cabecera de ejemplo

A continuación se muestra un ejemplo de la sintaxis general de la cabecera Authorization (para una solicitud con contenido en el cuerpo):

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 prueba

A continuación se muestra un par de claves de ejemplo, dos solicitudes de ejemplo y la cabecera Authorization resultante para cada uno.

Atención

Las firmas de ejemplo usan las claves RSA de 2048 bits que se muestran a continuación. Utilice estas claves solo para probar el código de firma, no para enviar solicitudes de producción.

-----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>"

Código de ejemplo

En esta sección se muestra el código básico de las solicitudes de API de firma.

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

Este código de ejemplo de Python necesita TLS 1.2, que no está incluido con Python por defecto en 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

En el siguiente ejemplo, se muestra cómo crear un firmante por defecto.

Nota

El SDK para Go expone un firmante autónomo que puede utilizar para firmar solicitudes personalizadas. Puede encontrar código relacionado en 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

Ver ejemplo de Bash en pantalla completa para facilitar la lectura.



#!/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}");
        }
    }
}