我正在使用PutObject上传文件,但我如何判断MD5校验和是否经过验证?

var s3Client = new AmazonS3Client();

string base64Checksum;
using (var md5 = MD5.Create())
{
    byte[] fileBytes = File.ReadAllBytes(filePath);
    byte[] hash = md5.ComputeHash(fileBytes, 0, fileBytes.Length);
    base64Checksum = Convert.ToBase64String(hash);
}

var putRequest = new PutObjectRequest()
{
    BucketName = bucketName,
    Key = objectKey,
    FilePath = filePath,
    ContentType = "application/txt",
    MD5Digest = base64Checksum 
};

await s3Client.PutObjectAsync(putRequest);

在响应中,ResponseMetadata.Checksum算法rithm被设置为NONE,ChecksumValidationStatus被设置为NOT_VALIDATED.

这是否意味着我提供的MD5散列尚未经过验证?

或者,如果我将Checksum算法rithm设置为Checksum算法rithm.SHA256:

var putRequest = new PutObjectRequest()
{
    // ...
    Checksum算法rithm = Checksum算法rithm.SHA256
};

校验和由AWS计算,但Checksum算法rithmChecksumValidationStatus仍保持不变.

即使我自己计算和设定:

var putRequest = new PutObjectRequest()
{
    // ...
    ChecksumSHA256 = sha256Checksum
};

我还是把Checksum算法rithm设置为NONE,ChecksumValidationStatusNOT_VALIDATED.

我做错了什么?

推荐答案

这是否意味着我提供的MD5散列尚未经过验证?

不,它已经过验证了.

Amazon S3根据通过Content-MD5标头发送的MD5校验和自动完成MD5校验和验证.

该值可以由SDK生成或作为PutObject请求的一部分提供,但关键是,无论谁提供MD5摘要-验证都由AWS完成,如docs:

使用此标头时,为Amazon S3 checks the object against the provided MD5 value,如果不匹配,则为Amazon S3 returns an error.

如果提供的MD5Digest是正确的(映射到Content-MD5报头),则PutObjectRequest成功,没有任何异常,表明MD5摘要已成功验证.

S3现在保证你上传的think是你上传的has.您的本地对象的MD5与S3-Great计算的MD5匹配.

现在,如果MD5Digest不正确(或上传已损坏),.NET SDK将抛出此异常,错误代码为BadDigest:

Amazon.S3.AmazonS3Exception: The Content-MD5 you specified did not match what we received.

这是所发生的事情的.NET版本,但请注意,SDK只是浮出了S3API的400 Bad Request错误.

默认情况下,SDK会为您生成MD5摘要.如果满足以下任一条件,则不会为您计算该值:

  • DisableMD5Stream设置为true
  • 已经为MD5Digest设置了一个值(对于您的代码来说,该条件为真)

你不需要提供你自己的价值.


或者,如果我将Checksum算法rithm设置为Checksum算法rithm.SHA256,则校验和由AWS计算

如果您将Checksum算法rithm设置为所需的算法,AWS SDK将计算校验和.然后,计算出的校验和将包含在发送到Amazon S3的请求中.

但是,此校验和不能与Amazon S3在收到请求后生成的校验和混淆.Amazon S3使用其校验和与请求中发送的校验和进行交叉引用.


撇开MD5校验和验证不谈,S3 announced在2022年2月支持4种新的校验和算法,可与MD5完整性判断一起使用:

  • CRC32:x-amz-checksum-crc32
  • CRC32C:x-amz-checksum-crc32c
  • SHA1:x-amz-checksum-sha1
  • SHA256:x-amz-checksum-sha256

如上所述,这些校验和也可以由SDK计算或由用户提供,但是,Amazon S3再次对照提供的校验和值判断对象,如果它们不匹配,则Amazon S3返回错误.

格式与上面相同:如果您的CRC32/CRC32C/SHA1/SHA256校验和值不正确,您将得到一个错误代码为BadDigest的异常,以及一条与您使用的任何校验和算法相关的消息.

Amazon.S3.AmazonS3Exception: The SHA256 you specified did not match the calculated checksum.

所有这些都与SDK无关.

SDK要么生成和发送生成的校验和值以及正确的头名称,要么只发送您手动为其提供正确头名称的校验和值,或者不发送头(不进行额外的校验和验证).


那么,ChecksumValidationStatus是什么呢?

如果你看看S3API的response object--所有SDK基本上都是它的客户--它实际上并不在那里.关于额外的校验和算法,它是.NET-SDK-specific concept,与MD5校验和验证相关的是not.

该字段为not,与Amazon S3的S校验和值相关.这由S3响应表示,在.NET SDK的例子中:无异常=✅有效&已验证的校验和.

因此,假设我们上传对象payroll.txt,其(伪)SHA256校验和值为a.没有异常被抛出,所以我们知道S3已经验证了我的对象在传输中没有被损坏,因为它们计算的校验和值payroll.txt也是a.我们现在确信S3真的像最初预期的那样存储了payroll.txt个.

在另一台设备上,我们通过.NET SDK发送GetObjectRequest来下载payroll.txt.我们知道S3真正存储的是payroll.txt,但是我们怎么知道.NET SDK真的按预期下载了payroll.txt呢?

这就是ChecksumValidationStatus发挥作用的地方,这应该在GetObjectRequest上进行判断.事实上,它甚至可以在PutObjectResponse上访问,这对我来说似乎是一个漏洞百出的抽象概念.

这就是为什么即使我们指定了一个额外的SHA256校验和值进行验证,PutObjectResponse的状态始终为NOT_VALIDATED.SDK客户端甚至不会validate对象在PutObject上的校验和,因此它甚至有意义拥有status.

有了这个字段,SDK就可以验证它是否下载了正确的对象&它是否在途中未被损坏.只要您已将请求的ChecksumMode设置为ChecksumMode.ENABLED,SDK就会获取和填充校验和字段,例如GetObjectResponse.ChecksumSHA256.ChecksumMode映射到x-amz-checksum-mode标头.

当然,然后您可以手动验证这一点,但SDK试图帮助将ChecksumValidationStatus的状态从PENDING_RESPONSE_READ(其初始值POST GET)更改为SUCCESSFULINVALID(基于它生成的散列).

它只能在完全读取后生成下载对象的散列,即在ResponseStream(标准.NET Stream)关闭时.

你可以在基于公共源代码的ChecksumValidationStatus enum的 comments 中看到这一点:

/// States for response checksum validation 
public enum ChecksumValidationStatus
{
    /// Set when the SDK did not perform checksum validation.
    NOT_VALIDATED,

    /// Set when a checksum was selected to be validated, but validation
    /// will not completed until the response stream is fully read. At that point an exception
    /// will be thrown if the checksum is invalid.
    PENDING_RESPONSE_READ,

    /// The checksum has been validated successfully during response unmarshalling.
    SUCCESSFUL,

    /// The checksum of the response stream did not match the header sent by the service.
    INVALID
}

我做错了什么?

似乎有一个bug,一旦蒸汽完全关闭,GetRequest上的验证状态永远不会从PENDING_RESPONSE_READ更改为SUCCESSFUL,甚至INVALID.

我的示例代码演示了这一点:

using Amazon.S3;
using Amazon.S3.Model;

var bucketName = "xyz";
var filePath = $"{DateTimeOffset.Now.ToUnixTimeMilliseconds()}.txt";

await File.WriteAllTextAsync(filePath, "my-test-content");

var s3Client = new AmazonS3Client();

var putObject = new PutObjectRequest()
{
    BucketName = bucketName,
    Key = filePath,
    FilePath = filePath,
    ContentType = "application/txt",
    Checksum算法rithm = Checksum算法rithm.SHA256,
};

Console.WriteLine($"Uploading object with key: {filePath}");
Console.WriteLine("---");
await s3Client.PutObjectAsync(putObject);

var getObject = new GetObjectRequest()
{
    BucketName = bucketName,
    Key = filePath,
    ChecksumMode = ChecksumMode.ENABLED,
};

Console.WriteLine($"Getting object with key: {filePath}");
Console.WriteLine("---");
var getResponse = await s3Client.GetObjectAsync(getObject);

Console.WriteLine($"GET response SHA256: {getResponse.ChecksumSHA256}");
Console.WriteLine($"Response stream CanRead status (not closed): {getResponse.ResponseStream.CanRead}");
Console.WriteLine($"GET response checksum validation status: {getResponse.ResponseMetadata.ChecksumValidationStatus}");
Console.WriteLine("---");


Console.WriteLine($"Reading stream...");
using (var reader = new StreamReader(getResponse.ResponseStream))
{
    var content = await reader.ReadToEndAsync();
    Console.WriteLine($"Stream contents: {content}");
}

Console.WriteLine("---");

Console.WriteLine($"Response stream CanRead status (not closed): {getResponse.ResponseStream.CanRead}");
Console.WriteLine($"GET response checksum validation status: {getResponse.ResponseMetadata.ChecksumValidationStatus}");

输出:

Uploading object with key: 1702578364813.txt
---
Getting object with key: 1702578364813.txt
---
GET response SHA256: q7IK7CFDRfD5yHQ4kFLUm6PaH1qQVdUvT+1jR3NAw/4=
Response stream CanRead status (not closed): True
GET响应校验和验证状态:PENDING_RESPONSE_READ
---
Reading stream...
Stream contents: my-test-content
---
Response stream CanRead status (not closed): False
GET响应校验和验证状态:PENDING_RESPONSE_READ

第二条:

GET响应校验和验证状态:PENDING_RESPONSE_READ

应该是:

获取响应校验和验证状态:成功

我已经向AWS SDK for.NET团队表明了这一点,以澄清为什么该属性没有更新.当我有答复时,我会更新问题的.


总而言之:

  • Amazon S3可以 Select 性地验证对象的MD5校验和和/或一个额外的校验和值,以确保上传的对象的完整性

  • 额外的校验和值可以是以下算法:MD5、CRC32、CRC32C、SHA1或SHA256

  • 校验和值可以由用户提供,也可以由SDK根据SDK配置生成

  • S3 API在PutObject请求上没有返回错误,表示S3已经成功验证了对象的校验和

  • SDK实现may提供了验证由API返回的校验和值的选项,只要SDK已经被配置为通过将x-amz-checksum-mode设置为ENABLED从S3获得校验和值

  • .NET SDK似乎有一个错误;AWS SDK for.NET团队现在正在调查这一问题

Csharp相关问答推荐

无法更改或使用C#(WinForms.NET)中的全局变量

如何在NServicebus中配置学习传输的文件夹(NService bus 8)

一种安全的方式来存储SSH凭证(MAUI/C#应用程序)

EF Core. Income和. AsNoTracking正确用法

自动映射程序在GroupBy之后使用项目

System.Net.Http.HttpClient.SendAsync(request)在docker容器内的POST方法30秒后停止

SortedSet.Remove()不会删除SortedSet.Min返回的元素

使用预定义对象减少Task.Run/Factory.StartNew中的关闭开销

.NET 8 DI GetServices<;对象&>不工作

在两个已具有一对多关系的表之间添加另一个一对多关系

.NET 8在appsettings.json中核心使用词典URI、URI&>

当我将`ConcurentDictionary`转换为`IDictionary`时,出现了奇怪的并发行为

C#LINQ延迟执行和嵌套方法

是否可以在Entity Framework Core中使用只读 struct 作为拥有实体?

将J数组转换为列表,只保留一个嵌套的JToken

映射器-如何映射到多个实体

将两个for循环更改为一条LINQ语句

Excel将';@';添加到具有范围的公式中

使用ITfoxtec.Identity.Saml2解析相同键多值SAML 2声明

在Swagger中显示自定义属性的属性名称