Berechnen von Prüfsummen - Amazon S3 Glacier

Diese Seite richtet sich nur an Bestandskunden des S3 Glacier-Dienstes, die Vaults und das Original REST API von 2012 verwenden.

Wenn Sie nach Archivspeicherlösungen suchen, empfehlen wir die Verwendung der S3 Glacier-Speicherklassen in Amazon S3, S3 Glacier Instant Retrieval, S3 Glacier Flexible Retrieval und S3 Glacier Deep Archive. Weitere Informationen zu diesen Speicheroptionen finden Sie unter S3 Glacier-Speicherklassen und Langfristige Datenspeicherung mit S3 Glacier-Speicherklassen im Amazon S3 S3-Benutzerhandbuch. Diese Speicherklassen verwenden Amazon S3API, sind in allen Regionen verfügbar und können in der Amazon S3 S3-Konsole verwaltet werden. Sie bieten Funktionen wie Speicherkostenanalyse, Storage Lens, erweiterte optionale Verschlüsselungsfunktionen und mehr.

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

Berechnen von Prüfsummen

Bei einem Archiv-Upload müssen die Header x-amz-sha256-tree-hash und x-amz-content-sha256 enthalten sein. Der x-amz-sha256-tree-hash-Header ist eine Prüfsumme der Nutzlast in Ihrem Anfragetext. In diesem Thema wird die Berechnung des x-amz-sha256-tree-hash-Headers beschrieben. Der x-amz-content-sha256-Header ist ein Hash der gesamten Nutzlast und ist für die Autorisierung erforderlich. Weitere Informationen finden Sie unter Beispiel für eine Signaturberechnung für Streaming API.

Die Nutzlast Ihrer Anfrage kann Folgendes sein:

  • Gesamtes Archiv: Wenn Sie ein Archiv in einer einzelnen Anforderung mit der API „Upload Archive“ hochladen, wird das gesamte Archiv im Anforderungstext gesendet. In diesem Fall muss die Prüfsumme des gesamten Archivs enthalten sein.

  • Archivteile: Wenn Sie ein Archiv mit der API „Multipart Upload“ in Teilen hochladen, senden Sie nur einen Teil des Archivs im Anforderungstext. In diesem Fall ist die Prüfsumme des Archivteils enthalten. Nachdem Sie alle Teile hochgeladen haben, senden Sie eine „Complete Multipart Upload“-Anfrage, in der die Prüfsumme des gesamten Archivs enthalten sein muss.

Die Prüfsumme für die Nutzlast ist ein SHA-256-Struktur-Hash. Dies wird als Struktur-Hash bezeichnet, da bei der Berechnung der Prüfsumme die Struktur eines SHA-256-Hash-Werts berechnet wird. Der Hash-Wert am Stamm ist die Prüfsumme des gesamten Archivs.

Anmerkung

In diesem Abschnitt wird eine Möglichkeit zur Berechnung des SHA-256-Struktur-Hashs beschrieben. Allerdings können Sie einen beliebigen Vorgang verwenden, bei dem das gleiche Ergebnis erzielt wird.

Der SHA-256-Struktur-Hash wird wie folgt berechnet:

  1. Berechnen Sie den SHA-256-Hash für jeden 1 MB großen Teil der Nutzlastdaten. Der letzte Datenanteil kann kleiner als 1 MB sein. Wenn Sie beispielsweise ein Archiv mit 3,2 MB hochladen, berechnen Sie die SHA-256-Hash-Werte für jeden der ersten drei 1 MB großen Datenstücke und berechnen dann den SHA-256-Hash der verbleibenden 0,2 MB Daten. Diese Hash-Werte formen die Blattknoten der Baumstruktur.

  2. Erstellen Sie die nächste Ebene der Baumstruktur.

    1. Verketten Sie die Hash-Werte von zwei aufeinanderfolgenden untergeordneten Knoten und berechnen Sie den SHA-256-Wert der verketteten Hash-Werte. Durch diese Verkettung und Erstellung des SHA-256-Hashs wird ein übergeordneten Knoten für die beiden untergeordneten Knoten erstellt.

    2. Wenn nur ein untergeordneter Knoten übrig bleibt, stufen Sie diesen Hash-Wert auf die nächste Ebene in der Baumstruktur hoch.

  3. Wiederholen Sie Schritt 2, bis die sich daraus ergebende Struktur einen Stamm hat. Der Stamm der Baumstruktur stellt einen Hash des gesamten Archivs bereit und ein Stamm der entsprechenden Nebenstruktur stellt den Hash des Teils eines mehrteiligen Uploads bereit.

Struktur-Hash-Beispiel 1: Hochladen eines Archivs in einer einzelnen Anfrage

Wenn Sie ein Archiv in einer einzelnen Anfrage mit der „Upload Archive“-API hochladen (Informationen dazu unter Upload Archive (POST archive)), ist das gesamte Archiv in der Anfragenutzlast enthalten. Dem entsprechend muss der Struktur-Hash des gesamten Archivs im x-amz-sha256-tree-hash-Anfrage-Header enthalten sein. Angenommen, Sie möchten ein Archiv mit 6,5 MB hochladen. Das folgende Diagramm verdeutlicht den Vorgang zur Erstellung eines SHA-256-Hashs für das Archiv. Sie lesen das Archiv und berechnen den SHA-256-Hash für jeden 1 MB große Teil. Sie berechnen den Hash für die 0,5 MB der verbleiben Daten und erstellen die Struktur wie in den folgenden Schritten beschrieben.

Diagramm mit einem Baum-Hash-Beispiel für das Hochladen eines Archivs in einer einzigen Anfrage.

Struktur-Hash-Beispiel 2: Hochladen eines Archivs mit einem mehrteiligen Upload

Der Vorgang zur Berechnung des Struktur-Hashs beim Upload eines Archivs mittels mehrteiligem Upload entspricht dem Archiv-Upload in einer einzelnen Anfrage. Der einzige Unterschied besteht darin, dass bei einem mehrteiligen Upload in jeder Anfrage (mit der Upload Part (PUT uploadID)-API) nur ein Teil des Archivs hochgeladen wird, und Sie daher die Prüfsumme nur dieses Teils im x-amz-sha256-tree-hash-Anfrage-Header bereitstellen. Nachdem alle Teile hochgeladen wurden, müssen Sie allerdings die „Complete Multipart Upload“-Anfrage senden (Informationen unter Complete Multipart Upload (POST uploadID)), wobei der Struktur-Hash des gesamten Archivs im x-amz-sha256-tree-hash-Anfrage-Header enthalten ist.

Diagramm mit einem Baum-Hash-Beispiel für das Hochladen eines Archivs mithilfe eines mehrteiligen Uploads.

Berechnen des Struktur-Hashs einer Datei

Die hier gezeigten Algorithmen wurden zu Demonstrationszwecken ausgewählt. Sie können den Code nach Bedarf für Ihr Implementierungsszenario optimieren. Wenn Sie ein Amazon-SDK zur Programmierung in Amazon S3 Glacier (S3 Glacier) verwenden, wird die Struktur-Hash-Berechnung für Sie ausgeführt und Sie müssen lediglich den Dateiverweis bereitstellen.

Beispiel 1: Java-Beispiel

Im folgenden Beispiel wird verdeutlicht, wie der SHA256-Struktur-Hash einer Datei mit Java berechnet wird. Sie können dieses Beispiel entweder ausführen, indem Sie den Speicherort einer Datei als ein Argument angeben, oder Sie können die TreeHashExample.computeSHA256TreeHash-Methode direkt aus Ihrem Code verwenden.

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

Im folgenden Beispiel wird verdeutlicht, wie der SHA256-Struktur-Hash einer Datei berechnet wird. Sie können dieses Beispiel ausführen, indem Sie den Speicherort der Datei als Argument angeben.

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