Amazon S3 でのオブジェクトの整合性のチェック - Amazon Simple Storage Service

Amazon S3 でのオブジェクトの整合性のチェック

Amazon S3 は、チェックサム値を使用して、アップロードまたはダウンロードするデータの整合性を検証します。さらに、Amazon S3 に保存するオブジェクトについて、別のチェックサム値を計算するようにリクエストすることもできます。データをアップロード、コピー、またはバッチコピーするときに使用するチェックサムアルゴリズムを選択できます。

データをアップロードすると、Amazon S3 は選択したアルゴリズムを使用してサーバー側でチェックサムを計算し、指定された値で検証してから、オブジェクトを保存し、チェックサムをオブジェクトメタデータの一部として保存します。この検証は、シングルパートアップロードとマルチパートアップロードの両方に対して、暗号化モード、オブジェクトサイズ、ストレージクラス間で一貫して機能します。ただし、データをコピーまたはバッチコピーすると、Amazon S3 はソースオブジェクトのチェックサムを計算し、送信先オブジェクトに移動します。

注記

シングルパートまたはマルチパートアップロードを実行する場合、オプションで、事前計算されたチェックサムをリクエストの一部として含め、フルオブジェクトチェックサムタイプを使用することができます。複数のオブジェクトで事前計算された値を使用するには、AWS CLI または AWS SDK を使用します。

サポートされているチェックサムアルゴリズムの使用

Amazon S3 では、チェックサムアルゴリズムを選択して、アップロード中にデータを検証できます。その後、指定されたチェックサムアルゴリズムがオブジェクトとともに保存され、ダウンロード中にデータの整合性を検証するために使用できます。チェックサム値を計算するには、セキュアハッシュアルゴリズム (SHA) または巡回冗長検査 (CRC) チェックアルゴリズムのいずれかを選択できます。

  • CRC-64/NVME (CRC64NVME)

  • CRC-32 (CRC32)

  • CRC-32C (CRC32C)

  • SHA-1 (SHA1)

  • SHA-256 (SHA256)

さらに、Content-MD5 ヘッダーを使用して、各リクエストにチェックサムを提供できます。

オブジェクトをアップロードするときに、使用するアルゴリズムを指定します。

  • AWS Management Console を使用するときには、使用するチェックサムアルゴリズムを選択します。オプションでオブジェクトのチェックサム値を指定できます。Amazon S3 は、オブジェクトを受信すると、指定したアルゴリズムを使用してチェックサムを計算します。2 つの値が一致しない場合、Amazon S3 はエラーを生成します。

  • SDK を使用するときには、以下について注意してください。

    • ChecksumAlgorithm パラメータを Amazon S3 で使用するアルゴリズムに設定します。事前に計算されたチェックサムが既にある場合は、チェックサム値を AWS SDK に渡すと、SDK はリクエストにその値を含めます。チェックサム値を渡さないか、チェックサムアルゴリズムを指定しない場合、SDK はチェックサム値を自動的に計算し、リクエストに含めて整合性の保護を提供します。個々のチェックサム値がチェックサムアルゴリズムの設定値と一致しない場合、Amazon S3 は BadDigest エラーでリクエストに失敗します。

    • アップグレードされた AWS SDK を使用している場合、SDK がチェックサムアルゴリズムを選択します。ただし、このチェックサムアルゴリズムは上書きできます。

    • チェックサムアルゴリズムを指定せず、SDK もチェックサムを計算しない場合、S3 は自動的に CRC-64/NVME (CRC64NVME) チェックサムアルゴリズムを選択します。

  • REST API を使用しているときにはx-amz-sdk-checksum-algorithm パラメータを使用しないでください。代わりに、アルゴリズム固有のヘッダーのいずれかを使用します (例: x-amz-checksum-crc32)。

Amazon S3 に既にアップロードされているオブジェクトにこれらのチェックサム値のいずれかを適用するには、オブジェクトをコピーして、既存のチェックサムアルゴリズムを使用するか、新しいチェックサムアルゴリズムを使用するかを指定します。アルゴリズムを指定しない場合、S3 は既存のアルゴリズムを使用します。ソースオブジェクトに指定されたチェックサムアルゴリズムまたはチェックサム値がない場合、Amazon S3 は CRC-64/NVME アルゴリズムを使用して送信先オブジェクトのチェックサム値を計算します。S3 バッチオペレーションを使用してオブジェクトをコピーするときに、チェックサムアルゴリズムを指定することもできます。

重要

複合 (またはパートレベル) チェックサムの [チェックサム] でマルチパートアップロードを使用する場合、マルチパートアップロードのパート番号は、連続しており 1 から始まる必要があります。連続しないパート番号でマルチパートアップロードリクエストを完了しようとすると、Amazon S3 は HTTP 500 Internal Server エラーを生成します。

フルオブジェクトおよび複合チェックサムタイプ

Amazon S3 では、次の 2 つのタイプのチェックサムがサポートされています。

  • フルオブジェクトチェックサム: フルオブジェクトチェックサムは、マルチパートアップロードのすべてのコンテンツに基づいて計算され、最初のパートの最初のバイトから最後のパートの最後のバイトまでのすべてのデータをカバーします。

    注記

    すべての PUT リクエストには、フルオブジェクトのチェックサムタイプが必要です。

  • 複合チェックサム: 複合チェックサムは、マルチパートアップロードの各パートの個々のチェックサムに基づいて計算されます。このアプローチでは、すべてのデータコンテンツに基づいてチェックサムを計算する代わりに、パートレベルのチェックサム (最初の部分から最後の部分まで) を集計して、完全なオブジェクトに対する単一の複合チェックサムを生成します。

    注記

    オブジェクトがマルチパートアップロードとしてアップロードされるとき、オブジェクトのエンティティタグ (ETag) はオブジェクト全体の MD5 ダイジェストではありません。代わりに、Amazon S3 は、アップロードされた個々のパートの MD5 ダイジェストを計算します。MD5 ダイジェストは、最終的なオブジェクトの ETag を決定するために使用されます。Amazon S3 は MD5 ダイジェストのバイトを連結し、これらの連結値の MD5 ダイジェストを計算します。ETag 作成最終ステップの間に、Amazon S3 はパートの総数を含むダッシュを末尾に追加します。

Amazon S3 は、次のフルオブジェクトおよび複合チェックサムアルゴリズムタイプをサポートしています。

  • CRC-64/NVME (CRC64NVME): フルオブジェクトのアルゴリズムタイプのみをサポートします。

  • CRC-32 (CRC32): フルオブジェクトアルゴリズムタイプと複合アルゴリズムタイプの両方をサポートします。

  • CRC-32C (CRC32C): フルオブジェクトアルゴリズムタイプと複合アルゴリズムタイプの両方をサポートします。

  • SHA-1 (SHA1): フルオブジェクトアルゴリズムタイプと複合アルゴリズムタイプの両方をサポートします。

  • SHA-256 (SHA256): フルオブジェクトアルゴリズムタイプと複合アルゴリズムタイプの両方をサポートします。

シングルパートアップロード

シングルパートでアップロード (PutObject を使用) されたオブジェクトのチェックサムは、フルオブジェクトチェックサムとして扱われます。Amazon S3 コンソールでオブジェクトをアップロードするときに、S3 で使用するチェックサムアルゴリズムを選択し、(オプションで) 事前に計算された値を指定することもできます。次に、Amazon S3 は、オブジェクトとそのチェックサム値を保存する前に、このチェックサムを検証します。オブジェクトのダウンロード中にチェックサム値をリクエストするときに、オブジェクトのデータ整合性を検証できます。

マルチパートアップロード

MultipartUpload API を使用してオブジェクトを複数のパートに分けてアップロードする場合、Amazon S3 で使用するチェックサムアルゴリズムとチェックサムタイプ (フルオブジェクトまたは複合) を指定できます。

次の表は、マルチパートアップロードの各チェックサムアルゴリズムでサポートされているチェックサムアルゴリズムタイプを示しています。

チェックサムアルゴリズム フルオブジェクト 複合
CRC-64/NVME (CRC64NVME) はい いいえ
CRC-32 (CRC32) あり あり
CRC-32C (CRC32C) あり あり
SHA-1 (SHA1) あり あり
SHA-256 (SHA256) あり あり

マルチパートアップロードへのフルオブジェクトチェックサムの使用

マルチパートアップロードを作成または実行するときは、アップロード時の検証にフルオブジェクトチェックサムを使用できます。つまり、MultipartUpload API にチェックサムアルゴリズムを提供できることを意味します。アップロードされたオブジェクトのパート境界を追跡する必要がなくなるため、整合性検証ツールを簡素化できます。オブジェクト全体のチェックサムとオブジェクトサイズを CompleteMultipartUpload リクエストで指定できます。

マルチパートアップロード中にフルオブジェクトチェックサムを指定すると、AWS SDK はそのチェックサムを Amazon S3 に渡し、S3 はオブジェクトの整合性をサーバー側で検証して、受信した値と比較します。次に、値が一致すると、Amazon S3 はオブジェクトを保存します。2 つの値が一致しない場合、S3 は BadDigest エラーでリクエストに失敗します。オブジェクトのチェックサムはオブジェクトメタデータにも保存され、後でオブジェクトのデータ整合性を検証するために使用します。

フルオブジェクトチェックサムには、S3 で CRC-64/NVME (CRC64NVME)、CRC-32 (CRC32)、または CRC-32C (CRC32C) チェックサムアルゴリズムを使用できます。マルチパートアップロードのフルオブジェクトチェックサムは、フルオブジェクトチェックサムに線形化できるため、CRC ベースのチェックサムでのみ使用できます。この線形化により、Amazon S3 はリクエストを並列化してパフォーマンスを向上させることができます。特に、S3 はパートレベルのチェックサムからオブジェクト全体のチェックサムを計算できます。このタイプの検証は、SHA や MD5 などの他のアルゴリズムでは使用できません。S3 にはデフォルトの整合性保護があるため、チェックサムなしでオブジェクトをアップロードすると、S3 は推奨されるフルオブジェクト CRC-64/NVME (CRC64NVME) チェックサムアルゴリズムをオブジェクトに自動的にアタッチします。

注記

マルチパートアップロードを開始するには、チェックサムアルゴリズムとフルオブジェクトチェックサムタイプを指定できます。チェックサムアルゴリズムとフルオブジェクトチェックサムタイプを指定した後、マルチパートアップロードのフルオブジェクトチェックサム値を指定できます。

マルチパートアップロードへのパートレベルのチェックサムの使用

オブジェクトが Amazon S3 にアップロードされるときには、単一のオブジェクトとしてアップロードされるか、マルチパートアップロードプロセスを使用してパートに分割してアップロードされます。マルチパートアップロードの [チェックサム] タイプを選択できます。マルチパートアップロードのパートレベルのチェックサム (または複合チェックサム) の場合、Amazon S3 は指定されたチェックサムアルゴリズムを使用して個々のパートのチェックサムを計算します。UploadPart を使用して、各パートのチェックサム値を指定できます。Amazon S3 コンソールでアップロードしようとしているオブジェクトが CRC-64/NVME (CRC64NVME) チェックサムアルゴリズムを使用するように設定されていて、16 MB を超える場合は、自動的にフルオブジェクトチェックサムとして指定されます。

次に、Amazon S3 は保存されたパートレベルのチェックサム値を使用して、各パートが正しくアップロードされたことを確認します。各パートのチェックサム (オブジェクト全体) を指定すると、S3 は各パートの保存されたチェックサム値を使用して、指定されたチェックサム値と比較し、フルオブジェクトチェックサムを内部的に計算します。これにより、S3 はパートのチェックサムを使用してオブジェクト全体のチェックサムを計算でき、コンピューティングコストが最小限に抑えられます。マルチパートアップロードの詳細については、「Amazon S3 でのマルチパートアップロードを使用したオブジェクトのアップロードとコピー」と「マルチパートアップロードへのフルオブジェクトチェックサムの使用」を参照してください。

オブジェクトが完全にアップロードされたら、最後に計算されたチェックサムを使用して、オブジェクトのデータ整合性を検証できます。

マルチパートアップロードの一部をアップロードするときは、次の点に注意してください。

  • オブジェクト全体を構成するパートの数など、オブジェクトに関する情報を取得するには、GetObjectAttributes オペレーションを使用します。追加のチェックサムを使用すると、パートのチェックサム値を含む個々のパートの情報を回復することもできます。

  • 完了したアップロードでは、GetObject または HeadObject オペレーションを使用して、シングルパートのパート番号またはバイト範囲を指定することにより、個々のパートのチェックサムを取得することもできます。マルチパートアップロード処理中の個別のパートのチェックサム値を取得する場合は、ListParts を使用できます。

  • Amazon S3 はマルチパートオブジェクトのチェックサムを計算するので、オブジェクトをコピーした場合、チェックサム値が変更されることがあります。SDK または REST API を使用しており、CopyObject を呼び出した場合、Amazon S3 は CopyObject API オペレーションのサイズ制限までオブジェクトをコピーします。Amazon S3 は、オブジェクトが単一のリクエストでアップロードされたか、マルチパートアップロードの一部としてアップロードされたかにかかわらず、このコピーを単一のアクションとして実行します。copy コマンドでは、オブジェクトのチェックサムは、完全なオブジェクトの直接チェックサムです。オブジェクトが最初にマルチパートアップロードを使用してアップロードされた場合、データに変更がない場合でも、チェックサム値が変更されます。

  • CopyObject API オペレーションのサイズ制限よりも大きいオブジェクトは、マルチパートアップロード copy コマンドを使用する必要があります。

  • AWS Management Console を使用していくつかの操作を実行するとき、オブジェクトのサイズが 16 MB より大きい場合、Amazon S3 はマルチパートアップロードを使用します。

チェックサムオペレーション

オブジェクトをアップロードした後、チェックサム値を取得し、事前計算済みまたは以前に保存された同じアルゴリズムタイプのチェックサム値と比較できます。次の例は、データの整合性を検証するために使用できるチェックサムオペレーションまたは方法を示しています。

コンソールの使用方法およびオブジェクトのアップロード時に使用するチェックサムアルゴリズムの指定方法の詳細については、「オブジェクトのアップロード」および「チュートリアル: チェックサムを追加して Amazon S3 のデータの整合性をチェックする」を参照してください。

次の例は、AWS SDK を使用して、マルチパートアップロードで大きなファイルをアップロードする方法、大きなファイルをダウンロードする方法、およびマルチパートアップロードファイルを検証する方法を示しています。ファイル検証にはすべて SHA-256 を使用しています。

Java
例: SHA-256 を使用して大きなファイルをアップロード、ダウンロード、および検証する

作業サンプルの作成およびテストの手順については、「AWS SDK for Java のデベロッパーガイド」の「使用開始」を参照してください。

import software.amazon.awssdk.auth.credentials.AwsCredentials; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.core.ResponseInputStream; import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest; import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm; import software.amazon.awssdk.services.s3.model.ChecksumMode; import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest; import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadResponse; import software.amazon.awssdk.services.s3.model.CompletedMultipartUpload; import software.amazon.awssdk.services.s3.model.CompletedPart; import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest; import software.amazon.awssdk.services.s3.model.CreateMultipartUploadResponse; import software.amazon.awssdk.services.s3.model.GetObjectAttributesRequest; import software.amazon.awssdk.services.s3.model.GetObjectAttributesResponse; import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.GetObjectResponse; import software.amazon.awssdk.services.s3.model.GetObjectTaggingRequest; import software.amazon.awssdk.services.s3.model.ObjectAttributes; import software.amazon.awssdk.services.s3.model.PutObjectTaggingRequest; import software.amazon.awssdk.services.s3.model.Tag; import software.amazon.awssdk.services.s3.model.Tagging; import software.amazon.awssdk.services.s3.model.UploadPartRequest; import software.amazon.awssdk.services.s3.model.UploadPartResponse; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Base64; import java.util.List; public class LargeObjectValidation { private static String FILE_NAME = "sample.file"; private static String BUCKET = "sample-bucket"; //Optional, if you want a method of storing the full multipart object checksum in S3. private static String CHECKSUM_TAG_KEYNAME = "fullObjectChecksum"; //If you have existing full-object checksums that you need to validate against, you can do the full object validation on a sequential upload. private static String SHA256_FILE_BYTES = "htCM5g7ZNdoSw8bN/mkgiAhXt5MFoVowVg+LE9aIQmI="; //Example Chunk Size - this must be greater than or equal to 5MB. private static int CHUNK_SIZE = 5 * 1024 * 1024; public static void main(String[] args) { S3Client s3Client = S3Client.builder() .region(Region.US_EAST_1) .credentialsProvider(new AwsCredentialsProvider() { @Override public AwsCredentials resolveCredentials() { return new AwsCredentials() { @Override public String accessKeyId() { return Constants.ACCESS_KEY; } @Override public String secretAccessKey() { return Constants.SECRET; } }; } }) .build(); uploadLargeFileBracketedByChecksum(s3Client); downloadLargeFileBracketedByChecksum(s3Client); validateExistingFileAgainstS3Checksum(s3Client); } public static void uploadLargeFileBracketedByChecksum(S3Client s3Client) { System.out.println("Starting uploading file validation"); File file = new File(FILE_NAME); try (InputStream in = new FileInputStream(file)) { MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); CreateMultipartUploadRequest createMultipartUploadRequest = CreateMultipartUploadRequest.builder() .bucket(BUCKET) .key(FILE_NAME) .checksumAlgorithm(ChecksumAlgorithm.SHA256) .build(); CreateMultipartUploadResponse createdUpload = s3Client.createMultipartUpload(createMultipartUploadRequest); List<CompletedPart> completedParts = new ArrayList<CompletedPart>(); int partNumber = 1; byte[] buffer = new byte[CHUNK_SIZE]; int read = in.read(buffer); while (read != -1) { UploadPartRequest uploadPartRequest = UploadPartRequest.builder() .partNumber(partNumber).uploadId(createdUpload.uploadId()).key(FILE_NAME).bucket(BUCKET).checksumAlgorithm(ChecksumAlgorithm.SHA256).build(); UploadPartResponse uploadedPart = s3Client.uploadPart(uploadPartRequest, RequestBody.fromByteBuffer(ByteBuffer.wrap(buffer, 0, read))); CompletedPart part = CompletedPart.builder().partNumber(partNumber).checksumSHA256(uploadedPart.checksumSHA256()).eTag(uploadedPart.eTag()).build(); completedParts.add(part); sha256.update(buffer, 0, read); read = in.read(buffer); partNumber++; } String fullObjectChecksum = Base64.getEncoder().encodeToString(sha256.digest()); if (!fullObjectChecksum.equals(SHA256_FILE_BYTES)) { //Because the SHA256 is uploaded after the part is uploaded; the upload is bracketed and the full object can be fully validated. s3Client.abortMultipartUpload(AbortMultipartUploadRequest.builder().bucket(BUCKET).key(FILE_NAME).uploadId(createdUpload.uploadId()).build()); throw new IOException("Byte mismatch between stored checksum and upload, do not proceed with upload and cleanup"); } CompletedMultipartUpload completedMultipartUpload = CompletedMultipartUpload.builder().parts(completedParts).build(); CompleteMultipartUploadResponse completedUploadResponse = s3Client.completeMultipartUpload( CompleteMultipartUploadRequest.builder().bucket(BUCKET).key(FILE_NAME).uploadId(createdUpload.uploadId()).multipartUpload(completedMultipartUpload).build()); Tag checksumTag = Tag.builder().key(CHECKSUM_TAG_KEYNAME).value(fullObjectChecksum).build(); //Optionally, if you need the full object checksum stored with the file; you could add it as a tag after completion. s3Client.putObjectTagging(PutObjectTaggingRequest.builder().bucket(BUCKET).key(FILE_NAME).tagging(Tagging.builder().tagSet(checksumTag).build()).build()); } catch (IOException | NoSuchAlgorithmException e) { e.printStackTrace(); } GetObjectAttributesResponse objectAttributes = s3Client.getObjectAttributes(GetObjectAttributesRequest.builder().bucket(BUCKET).key(FILE_NAME) .objectAttributes(ObjectAttributes.OBJECT_PARTS, ObjectAttributes.CHECKSUM).build()); System.out.println(objectAttributes.objectParts().parts()); System.out.println(objectAttributes.checksum().checksumSHA256()); } public static void downloadLargeFileBracketedByChecksum(S3Client s3Client) { System.out.println("Starting downloading file validation"); File file = new File("DOWNLOADED_" + FILE_NAME); try (OutputStream out = new FileOutputStream(file)) { GetObjectAttributesResponse objectAttributes = s3Client.getObjectAttributes(GetObjectAttributesRequest.builder().bucket(BUCKET).key(FILE_NAME) .objectAttributes(ObjectAttributes.OBJECT_PARTS, ObjectAttributes.CHECKSUM).build()); //Optionally if you need the full object checksum, you can grab a tag you added on the upload List<Tag> objectTags = s3Client.getObjectTagging(GetObjectTaggingRequest.builder().bucket(BUCKET).key(FILE_NAME).build()).tagSet(); String fullObjectChecksum = null; for (Tag objectTag : objectTags) { if (objectTag.key().equals(CHECKSUM_TAG_KEYNAME)) { fullObjectChecksum = objectTag.value(); break; } } MessageDigest sha256FullObject = MessageDigest.getInstance("SHA-256"); MessageDigest sha256ChecksumOfChecksums = MessageDigest.getInstance("SHA-256"); //If you retrieve the object in parts, and set the ChecksumMode to enabled, the SDK will automatically validate the part checksum for (int partNumber = 1; partNumber <= objectAttributes.objectParts().totalPartsCount(); partNumber++) { MessageDigest sha256Part = MessageDigest.getInstance("SHA-256"); ResponseInputStream<GetObjectResponse> response = s3Client.getObject(GetObjectRequest.builder().bucket(BUCKET).key(FILE_NAME).partNumber(partNumber).checksumMode(ChecksumMode.ENABLED).build()); GetObjectResponse getObjectResponse = response.response(); byte[] buffer = new byte[CHUNK_SIZE]; int read = response.read(buffer); while (read != -1) { out.write(buffer, 0, read); sha256FullObject.update(buffer, 0, read); sha256Part.update(buffer, 0, read); read = response.read(buffer); } byte[] sha256PartBytes = sha256Part.digest(); sha256ChecksumOfChecksums.update(sha256PartBytes); //Optionally, you can do an additional manual validation again the part checksum if needed in addition to the SDK check String base64PartChecksum = Base64.getEncoder().encodeToString(sha256PartBytes); String base64PartChecksumFromObjectAttributes = objectAttributes.objectParts().parts().get(partNumber - 1).checksumSHA256(); if (!base64PartChecksum.equals(getObjectResponse.checksumSHA256()) || !base64PartChecksum.equals(base64PartChecksumFromObjectAttributes)) { throw new IOException("Part checksum didn't match for the part"); } System.out.println(partNumber + " " + base64PartChecksum); } //Before finalizing, do the final checksum validation. String base64FullObject = Base64.getEncoder().encodeToString(sha256FullObject.digest()); String base64ChecksumOfChecksums = Base64.getEncoder().encodeToString(sha256ChecksumOfChecksums.digest()); if (fullObjectChecksum != null && !fullObjectChecksum.equals(base64FullObject)) { throw new IOException("Failed checksum validation for full object"); } System.out.println(fullObjectChecksum); String base64ChecksumOfChecksumFromAttributes = objectAttributes.checksum().checksumSHA256(); if (base64ChecksumOfChecksumFromAttributes != null && !base64ChecksumOfChecksums.equals(base64ChecksumOfChecksumFromAttributes)) { throw new IOException("Failed checksum validation for full object checksum of checksums"); } System.out.println(base64ChecksumOfChecksumFromAttributes); out.flush(); } catch (IOException | NoSuchAlgorithmException e) { //Cleanup bad file file.delete(); e.printStackTrace(); } } public static void validateExistingFileAgainstS3Checksum(S3Client s3Client) { System.out.println("Starting existing file validation"); File file = new File("DOWNLOADED_" + FILE_NAME); GetObjectAttributesResponse objectAttributes = s3Client.getObjectAttributes(GetObjectAttributesRequest.builder().bucket(BUCKET).key(FILE_NAME) .objectAttributes(ObjectAttributes.OBJECT_PARTS, ObjectAttributes.CHECKSUM).build()); try (InputStream in = new FileInputStream(file)) { MessageDigest sha256ChecksumOfChecksums = MessageDigest.getInstance("SHA-256"); MessageDigest sha256Part = MessageDigest.getInstance("SHA-256"); byte[] buffer = new byte[CHUNK_SIZE]; int currentPart = 0; int partBreak = objectAttributes.objectParts().parts().get(currentPart).size(); int totalRead = 0; int read = in.read(buffer); while (read != -1) { totalRead += read; if (totalRead >= partBreak) { int difference = totalRead - partBreak; byte[] partChecksum; if (totalRead != partBreak) { sha256Part.update(buffer, 0, read - difference); partChecksum = sha256Part.digest(); sha256ChecksumOfChecksums.update(partChecksum); sha256Part.reset(); sha256Part.update(buffer, read - difference, difference); } else { sha256Part.update(buffer, 0, read); partChecksum = sha256Part.digest(); sha256ChecksumOfChecksums.update(partChecksum); sha256Part.reset(); } String base64PartChecksum = Base64.getEncoder().encodeToString(partChecksum); if (!base64PartChecksum.equals(objectAttributes.objectParts().parts().get(currentPart).checksumSHA256())) { throw new IOException("Part checksum didn't match S3"); } currentPart++; System.out.println(currentPart + " " + base64PartChecksum); if (currentPart < objectAttributes.objectParts().totalPartsCount()) { partBreak += objectAttributes.objectParts().parts().get(currentPart - 1).size(); } } else { sha256Part.update(buffer, 0, read); } read = in.read(buffer); } if (currentPart != objectAttributes.objectParts().totalPartsCount()) { currentPart++; byte[] partChecksum = sha256Part.digest(); sha256ChecksumOfChecksums.update(partChecksum); String base64PartChecksum = Base64.getEncoder().encodeToString(partChecksum); System.out.println(currentPart + " " + base64PartChecksum); } String base64CalculatedChecksumOfChecksums = Base64.getEncoder().encodeToString(sha256ChecksumOfChecksums.digest()); System.out.println(base64CalculatedChecksumOfChecksums); System.out.println(objectAttributes.checksum().checksumSHA256()); if (!base64CalculatedChecksumOfChecksums.equals(objectAttributes.checksum().checksumSHA256())) { throw new IOException("Full object checksum of checksums don't match S3"); } } catch (IOException | NoSuchAlgorithmException e) { e.printStackTrace(); } } }

REST リクエストを送信して、チェックサムの値を持つオブジェクトをアップロードし、PutObject でデータの整合性を検証できます。GetObject または HeadObject を使用して、オブジェクトのチェックサムの値を取得することもできます。

単一のオペレーションで、最大 5 GB のオブジェクトをアップロードする PUT リクエストを送信できます。詳細については、「AWS CLI コマンドリファレンス」の PutObject を参照してください。また、get-objecthead-object を使用して、すでにアップロードされたオブジェクトのチェックサムを取得し、データの整合性を検証することもできます。

詳細については、「AWS Command Line Interface ユーザーガイド」の「Amazon S3 CLI のよくある質問」を参照してください。

オブジェクトをアップロードするときに Content-MD5 を使用する

アップロード後にオブジェクトの整合性を検証するもう 1 つの方法は、アップロード時にオブジェクトの MD5 ダイジェストを指定することです。オブジェクトの MD5 ダイジェストを計算した場合、Content-MD5 ヘッダーを使用することで、PUT コマンドでダイジェストを指定できます。

オブジェクトをアップロードした後、Amazon S3 はオブジェクトの MD5 ダイジェストを計算し、指定した値と比較します。リクエストは、2 つのダイジェストが一致した場合にのみ成功します。

MD5 ダイジェストを指定する必要はありませんが、アップロードプロセスの一環としてオブジェクトの整合性を検証するために使用できます。

Content-MD5 と ETag を使用して、アップロードされたオブジェクトを検証する

オブジェクトのエンティティタグ (ETag) は、そのオブジェクトの特定のバージョンを表します。ETag は、オブジェクトのコンテンツに加えられた変更のみを反映し、メタデータに加えられた変更は反映しないことに注意してください。オブジェクトのメタデータのみが変更された場合、ETag は同じままです。

オブジェクトによっては、オブジェクトの ETag がオブジェクトデータの MD5 ダイジェストである場合があります。

  • オブジェクトが PutObjectPostObject、または CopyObject オペレーションによって、または AWS Management Console を介して作成され、そのオブジェクトがプレーンテキストか、Amazon S3 マネージドキーを使用したサーバー側の暗号化 (SSE-S3) によって暗号化されている場合、そのオブジェクトの ETag は、オブジェクトデータの MD5 ダイジェストです。

  • オブジェクトが PutObjectPostObject、または CopyObject オペレーションによって、または AWS Management Console を介して作成され、そのオブジェクトがお客様が用意したキーを使用したサーバー側の暗号化 (SSE-C) または AWS Key Management Service (AWS KMS) キーを使用したサーバー側の暗号化 (SSE-KMS) によって暗号化されている場合、そのオブジェクトの ETag は、オブジェクトデータの MD5 ダイジェストではありません。

  • オブジェクトがマルチパートアップロードのプロセスまたは UploadPartCopy オペレーションによって作成された場合、暗号化の方法に関係なく、オブジェクトの ETag は MD5 ダイジェストではありません。オブジェクトが 16 MB より大きい場合、AWS Management Console はそのオブジェクトをマルチパートアップロードとしてアップロードまたはコピーするため、ETag は MD5 ダイジェストではありません。

ETag がオブジェクトの Content-MD5 ダイジェストであるオブジェクトの場合、オブジェクトの ETag 値を計算済みまたは以前に保存した Content-MD5 ダイジェストと比較できます。

追跡チェックサムの使用

Amazon S3 にオブジェクトをアップロードするときには、オブジェクトの事前計算されたチェックサムを指定するか、AWS SDK を使用して、チャンクアップロード用の追跡チェックサムを自動的に作成できます。追跡チェックサムを使用する場合、Amazon S3 は指定されたアルゴリズムを使用してチェックサムを自動的に生成し、オブジェクトのアップロード時に、チャンクアップロードでオブジェクトの整合性を検証します。

AWS SDK を使用しているときに追跡チェックサムを作成するには、ChecksumAlgorithm パラメータに任意のアルゴリズムを指定します。SDK は、そのアルゴリズムを使用してオブジェクト (またはオブジェクトパート) のチェックサムを計算し、チャンクアップロードリクエストの最後に自動的に追加します。この動作により、Amazon S3 はデータの検証とアップロードを単一のパスで実行するため、時間を節約できます。

重要

S3 オブジェクト Lambda を使用している場合、S3 オブジェクト Lambda へのすべてのリクエストは、s3 の代わりに s3-object-lambda を使用して署名されます。この動作は、追跡チェックサム値のシグネチャに影響します。S3 Object Lambda, の詳細については、「S3 Object Lambda を使用したオブジェクトの変換」を参照してください。

末尾のチェックサムヘッダー

チャンクコンテンツエンコードのリクエストを行うために、Amazon S3 では、リクエストを正しく解析するためにクライアントサーバーに複数のヘッダーを含める必要があります。クライアントサーバーには、次のヘッダーを含める必要があります。

  • x-amz-decoded-content-length: このヘッダーは、リクエストとともに Amazon S3 にアップロードされる実際のデータのプレーンテキストサイズを示します。

  • x-amz-content-sha256: このヘッダーは、リクエストに含まれるチャンクアップロードのタイプを示します。チェックサムが末尾にあるチャンクアップロードの場合、ヘッダー値は、ペイロード署名を使用しないリクエストの場合は STREAMING-UNSIGNED-PAYLOAD-TRAILER、SigV4 ペイロード署名を使用するリクエストの場合は STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER です。(署名付きペイロードの実装の詳細については、「Signature calculations for the authorization header: Transferring a payload in multiple chunks」を参照してください。)

  • x-amz-trailer: このヘッダーは、リクエストの末尾のヘッダーの名前を示します。末尾のチェックサムが存在する場合 (AWS SDK がエンコードされたリクエスト本文にチェックサムを追加する場合)、x-amz-trailer ヘッダー値には x-amz-checksum- プレフィックスが含まれ、アルゴリズム名で終わります。現在、次の x-amz-trailer 値がサポートされています。

    • x-amz-checksum-crc32

    • x-amz-checksum-crc32c

    • x-amz-checksum-crc64nvme

    • x-amz-checksum-sha1

    • x-amz-checksum-sha256

注記

リクエストには、チャンク化された値を含む Content-Encoding ヘッダーを含めることもできます。このヘッダーは必須ではありませんが、このヘッダーを含めることで、エンコードされたデータを送信する際の HTTP プロキシの問題を最小限に抑えることができます。リクエストに別の Content-Encoding ヘッダー (gzip など) が存在する場合、Content-Encoding ヘッダーには、エンコードのカンマ区切りリストにチャンク化された値が含まれます。例えば、Content-Encoding: aws-chunked, gzip と指定します。

チャンクパーツ

チャンクエンコードを使用して Amazon S3 にオブジェクトをアップロードする場合、アップロードリクエストには次のタイプのチャンク (リストされた順序でフォーマット) が含まれます。

  • オブジェクト本体のチャンク: チャンクアップロードリクエストに関連付けられた本体チャンクは、1 つ、複数、またはゼロの場合があります。

  • 完了チャンク: チャンクアップロードリクエストに関連付けられた本体チャンクは、1 つ、複数、またはゼロの場合があります。

  • 末尾チャンク: 末尾のチェックサムは、完了チャンクの後に表示されます。末尾のチャンクは 1 つだけ許可されます。

注記

チャンクアップロードはすべて、リクエストの終了を示すために、最後の CRLF (\r\n など) で終わる必要があります。

チャンク形式の例については、「例: 末尾にチェックサムが付いたチャンクアップロード」を参照してください。

オブジェクト本体のチャンク

オブジェクト本体のチャンクは、S3 にアップロードされる実際のオブジェクトデータを含むチャンクです。これらのチャンクには、一貫したサイズと形式の制約があります。

オブジェクト本体のチャンクサイズ

これらのチャンクには、少なくとも 8,192 バイト (または 8 KiB) のオブジェクトデータが含まれている必要があります。ただし、最後の本体チャンクは小さい場合があります。明示的な最大チャンクサイズはありませんが、すべてのチャンクが 5 GB の最大アップロードサイズより小さくなることが予想されます。チャンクサイズは、クライアントサーバーの実装に応じてチャンクごとに異なる場合があります。

オブジェクト本体のチャンク形式

オブジェクト本体のチャンクは、オブジェクト本体チャンク内のバイト数の 16 進エンコードで始まり、CRLF (キャリッジリターンラインフィード)、そのチャンクのオブジェクトバイト、および別の CRLF が続きます。

例:

hex-encoding-of-object-bytes-in-chunk\r\n chunk-object-bytes\r\n

ただし、チャンクが署名されている場合、オブジェクト本体のチャンクは別の形式に従います。この形式では、署名がセミコロン区切り文字でチャンクサイズに追加されます。例:

hex-encoding-of-object-bytes-in-chunk;chunk-signature\r\n chunk-object-bytes\r\n

チャンク署名の詳細については、「Signature calculations for the Authorization Header: Transferring a payload in multiple chunks (AWS Signature Version 4)」を参照してください。チャンクの形式の詳細については、RFC Editor ウェブサイトの「Chunked transfer encoding」を参照してください。

完了チャンク

完了チャンクは、すべてのチャンクアップロードの最後のオブジェクト本体チャンクである必要があります。完了チャンクの形式は本体チャンクに似ていますが、常に 0 バイトのオブジェクトデータが含まれています。(オブジェクトデータの 0 バイトは、すべてのデータがアップロードされたことを示します。) チャンクアップロードには、次のような形式に従って、最後のオブジェクト本体チャンクとして完了チャンクを含める必要があります。

0\r\n

ただし、コンテンツエンコードリクエストでペイロード署名が使用されている場合、代わりに次の形式に従います。

0;chunk-signature\r\n

トレーラーチャンク

トレーラーチャンクは、すべての S3 アップロードリクエストの計算されたチェックサムを保持します。トレーラーチャンクには、ヘッダー名フィールド 1 つとヘッダー値フィールド 1 つの 2 つのフィールドが含まれます。アップロードリクエストのヘッダー名フィールドは、x-amz-trailer リクエストヘッダーに渡された値と一致する必要があります。例えば、リクエストに x-amz-trailer: x-amz-checksum-crc32 が含まれており、トレーラーチャンクにヘッダー名 x-amz-checksum-sha1 がある場合、リクエストは失敗します。トレーラーチャンクの値フィールドには、そのオブジェクトのビッグエンディアンチェックサム値の base64 エンコードが含まれます。(ビッグエンディアンの順序では、最上位バイトのデータが最小のメモリアドレスに、最下位バイトが最大メモリアドレスに保存されます)。このチェックサムの計算に使用されるアルゴリズムは、ヘッダー名のサフィックス (例: crc32) と同じです。

トレーラーチャンク形式

トレーラーチャンクは、署名なしペイロードリクエストに次の形式を使用します。

x-amz-checksum-lowercase-checksum-algorithm-name:base64-checksum-value\n\r\n\r\n

SigV4 署名付きペイロードを使用したリクエストの場合、トレーラーチャンクには、トレーラーチャンクの後にトレーラー署名が含まれます。

trailer-checksum\n\r\n trailer-signature\r\n

また、CRLF を base64 チェックサム値の末尾に直接追加することもできます。例:

x-amz-checksum-lowercase-checksum-algorithm-name:base64-checksum-value\r\n\r\n

例: 末尾にチェックサムが付いたチャンクアップロード

Amazon S3 は、PutObjectaws-chunked コンテンツエンコードを使用するチャンクアップロードと、末尾のチェックサムを含む UploadPart リクエストをサポートします。

例 1 – 末尾に CRC-32 チェックサムが付いた署名なしチャンク PutObject リクエスト

以下は、末尾に CRC-32 チェックサムが付いたチャンク PutObject リクエストの例です。この例では、クライアントは 3 つの署名なしチャンクに 17 KB のオブジェクトをアップロードし、x-amz-checksum-crc32 ヘッダーを使用して末尾に CRC-32 チェックサムチャンクを追加します。

PUT /Key+ HTTP/1.1 Host: amzn-s3-demo-bucket Content-Encoding: aws-chunked x-amz-decoded-content-length: 17408 x-amz-content-sha256: STREAMING-UNSIGNED-PAYLOAD-TRAILER x-amz-trailer: x-amz-checksum-crc32 2000\r\n // Object body chunk 1 (8192 bytes) object-bytes\r\n 2000\r\n // Object body chunk 2 (8192 bytes) object-bytes\r\n 400\r\n // Object body chunk 3 (1024 bytes) object-bytes\r\n 0\r\n // Completion chunk x-amz-checksum-crc32:YABb/g==\n\r\n\r\n // Trailer chunk (note optional \n character) \r\n // CRLF

レスポンスの例を次に示します。

HTTP/1.1 200 ETag: ETag x-amz-checksum-crc32: YABb/g==
注記

チェックサム値の最後にあるラインフィード \n の使用は、クライアントによって異なる場合があります。

例 2 – 末尾に CRC-32 (CRC32) チェックサムが付いた SigV4 署名付きチャンク PutObject リクエスト

以下は、末尾に CRC-32 チェックサムが付いたチャンク PutObject リクエストの例です。このリクエストは SigV4 ペイロード署名を使用します。この例では、クライアントは 3 つの署名付きチャンクに 17 KB のオブジェクトをアップロードします。object body チャンクに加えて、completion chunktrailer chunk も署名されています。

PUT /Key+ HTTP/1.1 Host: amzn-s3-demo-bucket.s3.amazonaws.com Content-Encoding: aws-chunked x-amz-decoded-content-length: 17408 x-amz-content-sha256: STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER x-amz-trailer: x-amz-checksum-crc32 authorization-code // SigV4 headers authorization 2000;chunk-signature=signature-value...\r\n // Object body chunk 1 (8192 bytes) object-bytes\r\n 2000;chunk-signature\r\n // Object body chunk 2 (8192 bytes) object-bytes\r\n 400;chunk-signature\r\n // Object body chunk 3 (1024 bytes) object-bytes\r\n 0;chunk-signature\r\n // Completion chunk x-amz-checksum-crc32:YABb/g==\n\r\n // Trailer chunk (note optional \n character) trailer-signature\r\n \r\n // CRLF

レスポンスの例を次に示します。

HTTP/1.1 200 ETag: ETag x-amz-checksum-crc32: YABb/g==