Calcul des totaux de contrôle - Amazon S3 Glacier

Cette page s'adresse uniquement aux clients existants du service S3 Glacier utilisant Vaults et l'API REST d'origine datant de 2012.

Si vous recherchez des solutions de stockage d'archives, nous vous conseillons d'utiliser les classes de stockage S3 Glacier dans Amazon S3, S3 Glacier Instant Retrieval, S3 Glacier Flexible Retrieval et S3 Glacier Deep Archive. Pour en savoir plus sur ces options de stockage, consultez les sections Classes de stockage S3 Glacier et Stockage de données à long terme à l'aide des classes de stockage S3 Glacier dans le guide de l'utilisateur Amazon S3. Ces classes de stockage utilisent l'API Amazon S3, sont disponibles dans toutes les régions et peuvent être gérées au sein de la console Amazon S3. Ils offrent des fonctionnalités telles que l'analyse des coûts de stockage, Storage Lens, des fonctionnalités de sécurité, notamment de multiples options de cryptage, etc.

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Calcul des totaux de contrôle

Lors du chargement d'une archive, vous devez inclure les deux en-têtes x-amz-sha256-tree-hash et x-amz-content-sha256. L'en-tête x-amz-sha256-tree-hash est un total de contrôle de la charge utile dans le corps de votre demande. Cette rubrique explique comment calculer l'en-tête x-amz-sha256-tree-hash. L'en-tête x-amz-content-sha256 est un hachage de la totalité de la charge utile et est obligatoire pour l'autorisation. Pour plus d’informations, consultez Exemple de calcul de signature pour l'API de streaming.

La charge utile de votre demande peut être :

  • Une archive entière – Lorsque vous chargez une archive dans une même demande à l'aide de l'API de chargement d'archive, vous envoyez l'archive entière dans le corps de la demande. Dans ce cas, vous devez inclure le total de contrôle de l'intégralité de l'archive.

  • Une partie d'archive – Lorsque vous chargez une archive en plusieurs parties à l'aide de l'API de chargement partitionné, vous envoyez seulement une partie de l'archive dans le corps de la demande. Dans ce cas, vous incluez le total de contrôle de la partie de l'archive. Et, une fois que vous avez chargé toutes les parties, vous envoyez une demande d'achèvement de chargement partitionné qui doit inclure le total de contrôle de la totalité de l'archive.

Le total de contrôle de la charge utile est un hachage d'arborescence SHA-256. Il est appelé hachage d'arborescence car dans le processus de calcul du total de contrôle, vous calculez une arborescence des valeurs de hachage SHA-256. La valeur de hachage située à la racine est le total de contrôle de la totalité de l'archive.

Note

Cette section décrit une procédure permettant de calculer le hachage d'arborescence SHA-256. Vous pouvez toutefois utiliser n'importe quelle autre procédure à condition que celle-ci génère le même résultat.

Pour calculer le hachage d'arborescence SHA-256, procédez comme suit :

  1. Pour chaque bloc de 1 Mo des données de charge utile, calculez le hachage SHA-256. Le dernier bloc de données peut être inférieur à 1 Mo. Par exemple, si vous chargez une archive de 3,2 Mo, vous calculez les valeurs de hachage SHA-256 pour chacun des trois premiers blocs de 1 Mo de données, puis vous calculez le hachage SHA-256 pour le dernier bloc de 0,2 Mo de données. Ces valeurs de hachage constituent les nœuds terminaux de l'arborescence.

  2. Créez le niveau suivant de l'arborescence.

    1. Concaténez deux valeurs de hachage de nœud enfant consécutives et calculez le hachage SHA-256 des valeurs de hachage concaténées. Cette concaténation et la génération du hachage SHA-256 produisent un nœud parent pour les deux nœuds enfants.

    2. S'il ne reste qu'un nœud enfant, faites remonter cette valeur de hachage au niveau supérieur dans l'arborescence.

  3. Répétez l'étape 2 jusqu'à ce que l'arborescence obtenue possède une racine. La racine de l'arborescence fournit un hachage de la totalité de l'archive et une racine de la sous-arborescence appropriée fournit le hachage pour la partie dans le cadre d'un chargement partitionné.

Exemple de hachage d'arborescence 1 : Chargement d'une archive dans le cadre d'une seule demande

Lorsque vous chargez une archive dans le cadre d'une seule demande à l'aide de l'API de chargement d'archive (consultez la section Chargement d'archive (POST archive)), la charge utile de la demande comprend la totalité de l'archive. En conséquence, vous devez inclure le hachage d'arborescence de la totalité de l'archive dans l'en-tête de demande x-amz-sha256-tree-hash. Supposons que vous souhaitiez charger une archive de 6,5 Mo. Le schéma suivant illustre le processus de création du hachage SHA-256 de l'archive. Vous lisez l'archive et calculez le hachage SHA-256 pour chaque bloc de 1 Mo. Vous calculez également le hachage du dernier bloc de 0,5 Mo de données, puis vous créez l'arborescence comme indiqué dans la procédure précédente.

Schéma illustrant un exemple de hachage arborescent lors du téléchargement d'une archive en une seule demande.

Exemple de hachage d'arborescence 2 : chargement d'une archive à l'aide d'un chargement partitionné

Le processus de calcul du hachage d'arborescence lors du chargement d'une archive à l'aide du chargement partitionné est identique à celui utilisé lors du chargement de l'archive dans le cadre d'une seule demande. La seule différence réside dans le fait que pour un chargement partitionné, vous ne chargez qu'une partie de l'archive dans chaque demande (à l'aide de l'API Partie chargement (PUT uploadID)), et par conséquent, vous ne fournissez que le total de contrôle de la partie dans l'en-tête de demande x-amz-sha256-tree-hash. Cependant, une fois que vous avez chargé toutes les parties, vous devez envoyer la demande d'achèvement de chargement partitionné (consultez la section Achèvement du chargement partitionné (POST uploadID)) avec un hachage d'arborescence de la totalité de l'archive dans l'en-tête de demande x-amz-sha256-tree-hash.

Schéma illustrant un exemple de hachage arborescent lors du téléchargement d'une archive à l'aide d'un téléchargement en plusieurs parties.

Calcul du hachage d'arborescence d'un fichier

Les algorithmes présentés ici ont été sélectionnés à des fins de démonstration. Vous pouvez optimiser le code en fonction des besoins de votre scénario d'implémentation. Si vous utilisez un kit SDK Amazon afin de programmer pour Amazon S3 Glacier (S3 Glacier), le hachage d'arborescence est calculé automatiquement et il vous suffit de fournir la référence du fichier.

Exemple 1 : Exemple Java

L'exemple suivant montre comment calculer le hachage d'arborescence SHA256 d'un fichier à l'aide de Java. Vous pouvez exécuter cet exemple en fournissant un emplacement de fichier comme argument ou vous pouvez utiliser la méthode TreeHashExample.computeSHA256TreeHash directement à partir de votre code.

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(); } }
Exemple 2 : Exemple C# .NET

L'exemple suivant montre comment calculer le hachage d'arborescence SHA256 d'un fichier. Vous pouvez exécuter cet exemple en fournissant un emplacement de fichier comme argument.

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; } } }