Checksum Komputasi - Amazon S3 Glacier

Halaman ini hanya untuk pelanggan lama layanan S3 Glacier menggunakan Vaults dan REST API asli dari tahun 2012.

Jika Anda mencari solusi penyimpanan arsip, kami sarankan untuk menggunakan kelas penyimpanan S3 Glacier di Amazon S3, Pengambilan Instan Gletser S3, Pengambilan Fleksibel Gletser S3, dan S3 Glacier Deep Archive. Untuk mempelajari lebih lanjut tentang opsi penyimpanan ini, lihat Kelas penyimpanan S3 Glacier dan Penyimpanan data jangka panjang menggunakan kelas penyimpanan S3 Glacier di Panduan Pengguna Amazon S3. Kelas penyimpanan ini menggunakan Amazon S3 API, tersedia di semua wilayah, dan dapat dikelola dalam konsol Amazon S3. Mereka menawarkan fitur seperti Analisis Biaya Penyimpanan, Lensa Penyimpanan, fitur enkripsi opsional canggih, dan banyak lagi.

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

Checksum Komputasi

Saat mengunggah arsip, Anda harus menyertakan header x-amz-sha256-tree-hash dan x-amz-content-sha256. Header x-amz-sha256-tree-hash adalah checksum muatan di isi permintaan Anda. Topik ini menjelaskan cara menghitung header x-amz-sha256-tree-hash. Header x-amz-content-sha256 adalah hash seluruh muatan dan diperlukan untuk otorisasi. Untuk informasi selengkapnya, lihat Contoh Perhitungan Tanda Tangan untuk API Streaming.

Muatan permintaan Anda dapat berupa:

  • Seluruh arsip— Saat mengunggah arsip dalam satu permintaan menggunakan API Unggap Arsip, Anda mengirim seluruh arsip di isi permintaan. Dalam hal ini, Anda harus menyertakan checksum seluruh arsip.

  • Bagian arsip— Saat mengunggah arsip di bagian menggunakan API unggahan multipart, Anda hanya mengirim sebagian arsip di isi permintaan. Dalam hal ini, Anda menyertakan checksum bagian arsip. Dan setelah Anda mengunggah semua bagian, Anda mengirim permintaan Complete Multipart Upload (Selesaikan Unggahan Multipart), yang harus mencakup checksum seluruh arsip.

Checksum muatan adalah hash tree SHA-256. Ini disebut hash tree karena dalam proses komputasi checksum, Anda menghitung pohon nilai hash SHA-256. Nilai hash pada akar adalah checksum untuk seluruh arsip.

catatan

Bagian ini menjelaskan cara menghitung hash pohon SHA-256. Namun, Anda dapat menggunakan prosedur apa pun selama hasilnya yang sama.

Anda menghitung hash tree SHA-256 sebagai berikut:

  1. Untuk setiap potongan data muatan 1 MB, hitung hash SHA-256. Potongan data terakhir dapat kurang dari 1 MB. Misalnya, jika Anda mengunggah arsip berukuran 3,2 MB, Anda menghitung nilai hash SHA-256 untuk masing-masing tiga potongan data 1 MB pertama, lalu menghitung hash SHA-256 dari 0,2 MB data yang tersisa. Nilai hash ini membentuk node daun pokok.

  2. Bangun tingkat berikutnya dari pohon.

    1. Gabungkan dua nilai hash node turunan berturut-turut dan hitung hash SHA-256 dari gabungan nilai hash. Rangkaian dan pembuatan hash SHA-256 ini menghasilkan node induk untuk dua node turunan.

    2. Jika hanya satu node anak yang tersisa, promosikan nilai hash itu ke tingkat berikutnya di pohon.

  3. Ulangi langkah 2 sampai pohon yang dihasilkan memiliki akar. Akar pohon menyediakan hash seluruh arsip dan akar subpohom yang sesuai menyediakan hash untuk bagian dalam upload multipart.

Contoh Hash Pohon 1: Mengunggah arsip dalam satu permintaan

Saat Anda mengunggah arsip dalam permintaan tunggal menggunakan API Unggah Arsip (lihat Mengunggah Arsip (POST archive)), muatan permintaan menyertakan seluruh arsip. Dengan demikian, Anda harus menyertakan hash pohon seluruh arsip di header permintaan x-amz-sha256-tree-hash. Misalkan Anda ingin mengunggah arsip sebesar 6,5 MB. Diagram berikut menggambarkan proses pembuatan hash SHA-256 arsip. Anda membaca arsip dan menghitung hash SHA-256 untuk setiap potongan 1 MB. Anda juga menghitung hash untuk 0,5 MB data yang tersisa, lalu membangun pohon seperti yang diuraikan dalam prosedur sebelumnya.

Diagram yang menunjukkan contoh hash pohon mengunggah arsip dalam satu permintaan.

Contih Hash Pohon 2: Mengunggah arsip menggunakan unggahan multipart

Proses komputasi hash pohon saat mengunggah arsip menggunakan unggahan multipart sama seperti saat mengunggah arsip dalam permintaan tunggal. Satu-satunya perbedaan adalah dalam unggahan multipart, Anda hanya mengunggah sebagian arsip dalam setiap permintaan (menggunakan API Mengunggah Bagian (PUT UploadID)), dan karena itu, Anda memberikan checksum hanya bagian dalam header permintaan x-amz-sha256-tree-hash. Namun, setelah Anda mengunggah semua bagian, Anda harus mengirim permintaan Complete Multipart Upload (Selesaikan Unggahan Multipart) (lihat Menyelesaikan Unggahan Multipart (POST uploadID)) dengan pohon hash seluruh arsip di header permintaan x-amz-sha256-tree-hash.

Diagram yang menunjukkan contoh hash pohon mengunggah arsip menggunakan unggahan multipart.

Menghitung Hash Pohon File

Algoritma yang ditampilkan di sini dipilih untuk tujuan demonstrasi. Anda dapat mengoptimalkan kode yang diperlukan untuk skenario implementasi Anda. Jika Anda menggunakan SDK Amazon untuk memprogram Amazon S3 Glacier (S3 Glacier), perhitungan hash pohon dilakukan untuk Anda dan Anda hanya perlu memberikan referensi file.

contoh 1: Contoh Java

Contoh berikut menunjukkan cara menghitung hash pohon SHA256 file menggunakan Java. Anda dapat menjalankan contoh ini dengan menyediakan lokasi file sebagai argumen atau Anda dapat menggunakan metode TreeHashExample.computeSHA256TreeHash langsung dari kode Anda.

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

Contoh berikut menunjukkan cara menghitung hash pohon SHA256 dari file. Anda dapat menjalankan contoh ini dengan menyediakan lokasi file sebagai argumen.

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