Firme richiesta

Questo argomento descrive come firmare le richieste API di Oracle Cloud Infrastructure.

I campioni di firma sono inclusi per quanto segue:

Versione firma 1

La firma descritta qui è versione 1 della firma API di Oracle Cloud Infrastructure. In futuro, se Oracle modifica il metodo di firma delle richieste, il numero di versione verrà incrementato e l'azienda riceverà una notifica.

Credenziali e OCID richiesti

È necessaria una chiave di firma API nel formato corretto. Vedere Chiavi e OCID obbligatori.

Attenzione

Disallineamento clock client

Se l'orologio del client è distorto più di 5 minuti, viene restituito un codice di stato HTTP 401 (NotAuthenticated). Questa operazione avrà effetto sulle richieste API. Per ulteriori informazioni, vedere Skew clock client consentito massimo.

Sono inoltre necessari gli OCID per la tenancy e l'utente. Vedere Dove recuperare l'OCID della tenancy e l'OCID dell'utente.

Riepilogo dei passi di firma

In generale, questi sono i passaggi necessari per firmare una richiesta:

  1. Formare la richiesta HTTPS (è richiesto il protocollo SSL TLS 1.2).
  2. Creare la stringa di firma, che si basa su parti della richiesta.
  3. Creare la firma dalla stringa di firma, utilizzando la chiave privata e l'algoritmo RSA-SHA256.
  4. Aggiungere la firma risultante e altre informazioni obbligatorie all'intestazione Authorization nella richiesta.

Per informazioni dettagliate su questi passi, vedere le sezioni rimanenti di questo argomento.

Specifica con cui devi avere familiarità

Per informazioni su come eseguire i passi da 2 a 4 nel processo precedente, vedere draft-cavage-http-signatures-08. Si tratta di una bozza di specifica che costituisce la base per il modo in cui Oracle gestisce le firme delle richieste. Viene descritto in genere come formare la stringa di firma, come creare la firma e come aggiungere la firma e le informazioni richieste alla richiesta. Le restanti sezioni di questo argomento presuppongono che l'utente ne sia a conoscenza. Dettagli importanti dell'implementazione di Oracle Cloud Infrastructure del riferimento sono elencati nella sezione successiva.

Dettagli implementazione speciale

Le sezioni seguenti descrivono elementi importanti da notare sull'implementazione di Oracle Cloud Infrastructure della specifica.

Intestazione Autorizzazione

La firma di Oracle Cloud Infrastructure utilizza lo schema di autenticazione Signature (con un'intestazione Authorization) e non l'intestazione HTTP della firma.

Intestazioni obbligatorie

Questa sezione descrive le intestazioni che devono essere incluse nella stringa di firma.

Nota

Errore se manca l'intestazione obbligatoria

Se manca un'intestazione richiesta, il client riceverà una risposta "Non autorizzata" 401.

Per le richieste GET e DELETE (quando non c'è contenuto nel corpo della richiesta), la stringa di firma deve includere almeno queste intestazioni:

Per le richieste PUT e POST (quando c'è contenuto nel corpo della richiesta), la stringa di firma deve includere almeno queste intestazioni:

  • (request-target)
  • host
  • date o x-date (se entrambi sono inclusi, Oracle utilizza x-date)
  • x-content-sha256 (ad eccezione delle richieste PUT di storage degli oggetti; vedere la sezione successiva)
  • content-type
  • content-length
Attenzione

Per le richieste PUT e POST, il client deve calcolare il valore x-content-sha256 e includerlo nella stringa di richiesta e firma, anche se il corpo è una stringa vuota. Inoltre, il content-length è sempre richiesto nella stringa di richiesta e firma, anche se il corpo è vuoto. Alcuni client HTTP non invieranno il file content-length se il corpo è vuoto, quindi è necessario assicurarsi esplicitamente che il client lo invii. Se date e x-date sono entrambi inclusi, Oracle utilizza x-date. Il x-date viene utilizzato per proteggere dal riutilizzo della parte firmata della richiesta (riproduzione di attacchi).

L'unica eccezione riguarda le richieste PUT di storage degli oggetti sugli oggetti (vedere la sezione successiva).

Istruzioni speciali per il PUT di storage degli oggetti

Per le richieste PUT di storage degli oggetti PutObject e UploadPart, la stringa di firma deve includere almeno le intestazioni riportate di seguito.

  • (request-target)
  • host
  • date o x-date (se entrambi sono inclusi, Oracle utilizza x-date)

Se la richiesta include anche una qualsiasi delle altre intestazioni normalmente richieste per le richieste PUT (vedere l'elenco precedente), tali intestazioni devono essere incluse anche nella stringa di firma.

Caso e ordine delle intestazioni

Le intestazioni devono essere tutte minuscole nella stringa di firma.

L'ordine delle intestazioni nella stringa di firma non ha importanza. Assicurarsi di specificare l'ordine nel parametro headers nell'intestazione Authorization, come descritto in draft-cavage-http-signatures-05.

Attenzione

(request-target) include il percorso e la stringa di query della richiesta. Oracle prevede di creare la stringa di firma con i parametri di query nello stesso ordine visualizzato nella richiesta. Se i parametri della query di richiesta cambiano ordine dopo la firma, l'autenticazione non riuscirà.

Codifica URL di percorso e stringa di query

Quando si forma la stringa di firma, è necessario che l'URL codifichi tutti i parametri nel percorso e nella stringa di query (ma non le intestazioni) in base a RFC 3986.

Identificativo chiave

È necessario impostare keyId="<TENANCY OCID>/<USER OCID>/<KEY FINGERPRINT>" nell'intestazione Authorization aggiunta alla richiesta. Per recuperare questi valori, vedere Dove recuperare l'OCID della tenancy e l'OCID dell'utente. Un esempio di keyId è simile a questo (avvolto per adattarsi meglio alla pagina):

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

Algoritmo di firma

L'algoritmo di firma deve essere RSA-SHA256 ed è necessario impostare algorithm="rsa-sha256" nell'intestazione Authorization (avvisare le virgolette).

Versione firma

È necessario includere version="1" nell'intestazione Authorization (notare le virgolette). In caso contrario, si suppone che si stia utilizzando qualsiasi versione corrente (che è la versione 1 in questo momento).

Intestazione di esempio

Ecco un esempio della sintassi generale dell'intestazione Authorization (per una richiesta con contenuto nel 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>))"

Valori di test

Di seguito è riportata una coppia di chiavi di esempio, due richieste di esempio e l'intestazione Authorization risultante per ciascuna.

Attenzione

Le firme di esempio utilizzano le chiavi a 2048 bit RSA riportate di seguito. Utilizzare queste chiavi solo per testare il codice di firma, non per inviare richieste di produzione.

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

Codice di esempio

Questa sezione mostra il codice di base per la firma delle richieste 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

Questo codice di esempio Python richiede TLS 1.2, che non è incluso nel Python predefinito su 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());
})();

Rubino


# 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

Esegui

L'esempio seguente mostra come creare un firmatario predefinito.

Nota

SDK for Go espone un firmatario standalone che è possibile utilizzare per firmare richieste personalizzate. Il codice correlato è disponibile all'indirizzo 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

Visualizza l'esempio di Bash a schermo intero per una lettura più semplice.



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

N. 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}");
        }
    }
}