Calcolo di checksum - Amazon S3 Glacier

Questa pagina è riservata ai clienti esistenti del servizio S3 Glacier che utilizzano Vaults e l'originale del 2012. REST API

Se stai cercando soluzioni di archiviazione, ti consigliamo di utilizzare le classi di storage S3 Glacier in Amazon S3, S3 Glacier Instant Retrieval, S3 Glacier Flexible Retrieval e S3 Glacier Deep Archive. Per ulteriori informazioni su queste opzioni di storage, consulta le classi di storage S3 Glacier e lo storage dei dati a lungo termine con le classi di storage S3 Glacier nella Amazon S3 User Guide. Queste classi di storage utilizzano Amazon S3API, sono disponibili in tutte le regioni e possono essere gestite all'interno della console Amazon S3. Offrono funzionalità come Storage Cost Analysis, Storage Lens, funzionalità di crittografia opzionali avanzate e altro ancora.

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

Calcolo di checksum

Durante il caricamento di un archivio, devi includere le intestazioni x-amz-sha256-tree-hash e x-amz-content-sha256. L'intestazione x-amz-sha256-tree-hash è un checksum del payload nel corpo della richiesta. Questo argomento descrive come calcolare l'intestazione x-amz-sha256-tree-hash. L'intestazione x-amz-content-sha256 è un hash dell'intero payload ed è necessaria per l'autorizzazione. Per ulteriori informazioni, consulta Esempio di calcolo delle firme per lo streaming API.

Il payload della richiesta può essere:

  • Archivio completo: durante il caricamento di un archivio in un'unica richiesta mediante l'API Upload Archive, l'intero archivio viene inviato nel corpo della richiesta. In questo caso, devi includere il checksum dell'intero archivio.

  • Parte di un archivio: durante il caricamento di un archivio in parti mediante l'API corrispondente, solo una parte dell'archivio viene inviata nel corpo della richiesta. In questo caso, devi includere il checksum di quella parte dell'archivio. Dopo aver caricato tutte le parti, è necessario inviare una richiesta Complete Multipart Upload che deve includere il checksum dell'intero archivio.

Il checksum del payload è una struttura hash SHA-256. È denominata struttura hash in quanto nel processo di calcolo del checksum viene calcolata una struttura di valori hash SHA-256. Il valore hash nella radice è il checksum dell'intero archivio.

Nota

Questa sezione descrive un metodo di calcolo della struttura hash SHA-256. Tuttavia, puoi utilizzare qualsiasi altra procedura purché produca lo stesso risultato.

Per calcolare la struttura hash SHA-256, procedi come segue:

  1. Per ogni blocco di 1 MB di dati di payload, calcola l'hash SHA-256. L'ultimo blocco di dati può essere inferiore a 1 MB. Ad esempio, se stai caricando un archivio di 3,2 MB, calcoli i valori hash SHA-256 per ognuno dei primi tre blocchi di 1 MB di dati, quindi calcoli l'hash SHA-256 dei restanti 0,2 MB di dati. Questi valori hash formano i nodi foglia della struttura.

  2. Crea il livello successivo della struttura.

    1. Concatena i valori hash di due nodi figlio consecutivi e calcola l'hash SHA-256 dei valori hash concatenati. Questa concatenazione e generazione dell'hash SHA-256 produce un nodo padre per i due nodi figlio.

    2. Quando rimane un solo nodo figlio, alza il valore hash al livello successivo nella struttura.

  3. Ripeti la fase 2 fino a che la struttura risultante ha una radice. La radice della struttura fornisce un hash dell'intero archivio e una radice della sottostruttura appropriata fornisce l'hash della parte in un caricamento in più parti.

Esempio di struttura hash 1: caricamento di un archivio in un'unica richiesta

Quando carichi un archivio in un'unica richiesta mediante l'API Upload Archive (consulta Upload Archive (POST archive)), il payload della richiesta include l'intero archivio. Di conseguenza, devi includere la struttura hash dell'intero archivio nell'intestazione di richiesta x-amz-sha256-tree-hash. Supponi di voler caricare un archivio di 6,5 MB. Il diagramma seguente illustra il processo di creazione dell'hash SHA-256 dell'archivio. Leggi l'archivio e calcola l'hash SHA-256 per ogni blocco di 1 MB. Calcola anche l'hash per i restanti 0,5 MB di dati e quindi crea la struttura come descritto nella procedura precedente.

Diagramma che mostra un esempio di tree hash durante il caricamento di un archivio in una singola richiesta.

Esempio di struttura hash 2: caricamento di un archivio mediante un caricamento in più parti

Il processo di calcolo della struttura hash durante il caricamento di un archivio mediante il caricamento in più parti è identico a quello per il caricamento dell'archivio in un'unica richiesta. L'unica differenza è che in un caricamento in più parti carichi solo una parte dell'archivio in ogni richiesta (utilizzando l'API Upload Part (PUT uploadID)), di conseguenza fornisci soltanto il checksum della parte nell'intestazione di richiesta x-amz-sha256-tree-hash. Tuttavia, dopo il caricamento di tutte le parti, devi inviare la richiesta Complete Multipart Upload (vedi Complete Multipart Upload (POST uploadID)) con una struttura hash dell'intero archivio nell'intestazione di richiesta x-amz-sha256-tree-hash.

Diagramma che mostra un esempio di tree hash durante il caricamento di un archivio utilizzando un caricamento in più parti.

Calcolo della struttura hash di un file

Gli algoritmi illustrati di seguito sono selezionati a scopo dimostrativo. Puoi ottimizzare il codice in funzione delle esigenze del tuo scenario di implementazione. Se utilizzi un Amazon SDK per programmare in Amazon S3 Glacier (S3 Glacier), il calcolo della struttura hash viene eseguito automaticamente e devi solo specificare il riferimento al file.

Esempio 1. Esempio con Java

L'esempio seguente mostra come calcolare la struttura hash SHA256 di un file utilizzando Java. Puoi eseguire questo esempio specificando una percorso di file come argomento oppure utilizzare il metodo TreeHashExample.computeSHA256TreeHash direttamente nel codice.

import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class TreeHashExample { static final int ONE_MB = 1024 * 1024; /** * Compute the Hex representation of the SHA-256 tree hash for the specified * File * * @param args * args[0]: a file to compute a SHA-256 tree hash for */ public static void main(String[] args) { if (args.length < 1) { System.err.println("Missing required filename argument"); System.exit(-1); } File inputFile = new File(args[0]); try { byte[] treeHash = computeSHA256TreeHash(inputFile); System.out.printf("SHA-256 Tree Hash = %s\n", toHex(treeHash)); } catch (IOException ioe) { System.err.format("Exception when reading from file %s: %s", inputFile, ioe.getMessage()); System.exit(-1); } catch (NoSuchAlgorithmException nsae) { System.err.format("Cannot locate MessageDigest algorithm for SHA-256: %s", nsae.getMessage()); System.exit(-1); } } /** * Computes the SHA-256 tree hash for the given file * * @param inputFile * a File to compute the SHA-256 tree hash for * @return a byte[] containing the SHA-256 tree hash * @throws IOException * Thrown if there's an issue reading the input file * @throws NoSuchAlgorithmException */ public static byte[] computeSHA256TreeHash(File inputFile) throws IOException, NoSuchAlgorithmException { byte[][] chunkSHA256Hashes = getChunkSHA256Hashes(inputFile); return computeSHA256TreeHash(chunkSHA256Hashes); } /** * Computes a SHA256 checksum for each 1 MB chunk of the input file. This * includes the checksum for the last chunk even if it is smaller than 1 MB. * * @param file * A file to compute checksums on * @return a byte[][] containing the checksums of each 1 MB chunk * @throws IOException * Thrown if there's an IOException when reading the file * @throws NoSuchAlgorithmException * Thrown if SHA-256 MessageDigest can't be found */ public static byte[][] getChunkSHA256Hashes(File file) throws IOException, NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance("SHA-256"); long numChunks = file.length() / ONE_MB; if (file.length() % ONE_MB > 0) { numChunks++; } if (numChunks == 0) { return new byte[][] { md.digest() }; } byte[][] chunkSHA256Hashes = new byte[(int) numChunks][]; FileInputStream fileStream = null; try { fileStream = new FileInputStream(file); byte[] buff = new byte[ONE_MB]; int bytesRead; int idx = 0; int offset = 0; while ((bytesRead = fileStream.read(buff, offset, ONE_MB)) > 0) { md.reset(); md.update(buff, 0, bytesRead); chunkSHA256Hashes[idx++] = md.digest(); offset += bytesRead; } return chunkSHA256Hashes; } finally { if (fileStream != null) { try { fileStream.close(); } catch (IOException ioe) { System.err.printf("Exception while closing %s.\n %s", file.getName(), ioe.getMessage()); } } } } /** * Computes the SHA-256 tree hash for the passed array of 1 MB chunk * checksums. * * This method uses a pair of arrays to iteratively compute the tree hash * level by level. Each iteration takes two adjacent elements from the * previous level source array, computes the SHA-256 hash on their * concatenated value and places the result in the next level's destination * array. At the end of an iteration, the destination array becomes the * source array for the next level. * * @param chunkSHA256Hashes * An array of SHA-256 checksums * @return A byte[] containing the SHA-256 tree hash for the input chunks * @throws NoSuchAlgorithmException * Thrown if SHA-256 MessageDigest can't be found */ public static byte[] computeSHA256TreeHash(byte[][] chunkSHA256Hashes) throws NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[][] prevLvlHashes = chunkSHA256Hashes; while (prevLvlHashes.length > 1) { int len = prevLvlHashes.length / 2; if (prevLvlHashes.length % 2 != 0) { len++; } byte[][] currLvlHashes = new byte[len][]; int j = 0; for (int i = 0; i < prevLvlHashes.length; i = i + 2, j++) { // If there are at least two elements remaining if (prevLvlHashes.length - i > 1) { // Calculate a digest of the concatenated nodes md.reset(); md.update(prevLvlHashes[i]); md.update(prevLvlHashes[i + 1]); currLvlHashes[j] = md.digest(); } else { // Take care of remaining odd chunk currLvlHashes[j] = prevLvlHashes[i]; } } prevLvlHashes = currLvlHashes; } return prevLvlHashes[0]; } /** * Returns the hexadecimal representation of the input byte array * * @param data * a byte[] to convert to Hex characters * @return A String containing Hex characters */ public static String toHex(byte[] data) { StringBuilder sb = new StringBuilder(data.length * 2); for (int i = 0; i < data.length; i++) { String hex = Integer.toHexString(data[i] & 0xFF); if (hex.length() == 1) { // Append leading zero. sb.append("0"); } sb.append(hex); } return sb.toString().toLowerCase(); } }
Esempio 2. Esempio con C# .NET

L'esempio seguente mostra come calcolare la struttura hash SHA256 di un file. Puoi eseguire questo esempio specificando un percorso di file come argomento.

using System; using System.IO; using System.Security.Cryptography; namespace ExampleTreeHash { class Program { static int ONE_MB = 1024 * 1024; /** * Compute the Hex representation of the SHA-256 tree hash for the * specified file * * @param args * args[0]: a file to compute a SHA-256 tree hash for */ public static void Main(string[] args) { if (args.Length < 1) { Console.WriteLine("Missing required filename argument"); Environment.Exit(-1); } FileStream inputFile = File.Open(args[0], FileMode.Open, FileAccess.Read); try { byte[] treeHash = ComputeSHA256TreeHash(inputFile); Console.WriteLine("SHA-256 Tree Hash = {0}", BitConverter.ToString(treeHash).Replace("-", "").ToLower()); Console.ReadLine(); Environment.Exit(-1); } catch (IOException ioe) { Console.WriteLine("Exception when reading from file {0}: {1}", inputFile, ioe.Message); Console.ReadLine(); Environment.Exit(-1); } catch (Exception e) { Console.WriteLine("Cannot locate MessageDigest algorithm for SHA-256: {0}", e.Message); Console.WriteLine(e.GetType()); Console.ReadLine(); Environment.Exit(-1); } Console.ReadLine(); } /** * Computes the SHA-256 tree hash for the given file * * @param inputFile * A file to compute the SHA-256 tree hash for * @return a byte[] containing the SHA-256 tree hash */ public static byte[] ComputeSHA256TreeHash(FileStream inputFile) { byte[][] chunkSHA256Hashes = GetChunkSHA256Hashes(inputFile); return ComputeSHA256TreeHash(chunkSHA256Hashes); } /** * Computes a SHA256 checksum for each 1 MB chunk of the input file. This * includes the checksum for the last chunk even if it is smaller than 1 MB. * * @param file * A file to compute checksums on * @return a byte[][] containing the checksums of each 1MB chunk */ public static byte[][] GetChunkSHA256Hashes(FileStream file) { long numChunks = file.Length / ONE_MB; if (file.Length % ONE_MB > 0) { numChunks++; } if (numChunks == 0) { return new byte[][] { CalculateSHA256Hash(null, 0) }; } byte[][] chunkSHA256Hashes = new byte[(int)numChunks][]; try { byte[] buff = new byte[ONE_MB]; int bytesRead; int idx = 0; while ((bytesRead = file.Read(buff, 0, ONE_MB)) > 0) { chunkSHA256Hashes[idx++] = CalculateSHA256Hash(buff, bytesRead); } return chunkSHA256Hashes; } finally { if (file != null) { try { file.Close(); } catch (IOException ioe) { throw ioe; } } } } /** * Computes the SHA-256 tree hash for the passed array of 1MB chunk * checksums. * * This method uses a pair of arrays to iteratively compute the tree hash * level by level. Each iteration takes two adjacent elements from the * previous level source array, computes the SHA-256 hash on their * concatenated value and places the result in the next level's destination * array. At the end of an iteration, the destination array becomes the * source array for the next level. * * @param chunkSHA256Hashes * An array of SHA-256 checksums * @return A byte[] containing the SHA-256 tree hash for the input chunks */ public static byte[] ComputeSHA256TreeHash(byte[][] chunkSHA256Hashes) { byte[][] prevLvlHashes = chunkSHA256Hashes; while (prevLvlHashes.GetLength(0) > 1) { int len = prevLvlHashes.GetLength(0) / 2; if (prevLvlHashes.GetLength(0) % 2 != 0) { len++; } byte[][] currLvlHashes = new byte[len][]; int j = 0; for (int i = 0; i < prevLvlHashes.GetLength(0); i = i + 2, j++) { // If there are at least two elements remaining if (prevLvlHashes.GetLength(0) - i > 1) { // Calculate a digest of the concatenated nodes byte[] firstPart = prevLvlHashes[i]; byte[] secondPart = prevLvlHashes[i + 1]; byte[] concatenation = new byte[firstPart.Length + secondPart.Length]; System.Buffer.BlockCopy(firstPart, 0, concatenation, 0, firstPart.Length); System.Buffer.BlockCopy(secondPart, 0, concatenation, firstPart.Length, secondPart.Length); currLvlHashes[j] = CalculateSHA256Hash(concatenation, concatenation.Length); } else { // Take care of remaining odd chunk currLvlHashes[j] = prevLvlHashes[i]; } } prevLvlHashes = currLvlHashes; } return prevLvlHashes[0]; } public static byte[] CalculateSHA256Hash(byte[] inputBytes, int count) { SHA256 sha256 = System.Security.Cryptography.SHA256.Create(); byte[] hash = sha256.ComputeHash(inputBytes, 0, count); return hash; } } }