gpt4 book ai didi

amazon-s3 - 发出 S3 GET 请求时收到 400 Bad request

转载 作者:行者123 更新时间:2023-12-04 10:15:09 25 4
gpt4 key购买 nike

我正在尝试向 S3 发出 AWS 版本 4 授权签名的 GET 请求,并收到错误请求错误 400 Code:InvalidRequest Message:Missing required header对于这个请求:x-amz-content-sha256
如果我在标题前加上 "Authorization: " , 我得到错误 Code:InvalidArgument Message:Unsupported Authorization Type <ArgumentName>Authorization</ArgumentName> <ArgumentValue>Authorization: AWS4-HMAC-SHA256 Credential=XXXXXXXXXXXXXXXXXXX/20200408/eu-west-3/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=vdchzint97uwyt3g%2fjehszrc8zpkbjsx4tfqacsqfow%3d</ArgumentValue>
我正在使用 Delphi XE5 和 Indy 的 TIdHTTP零件。谁能告诉我我做错了什么?我在下面包含了我的代码。

begin
bucket := 'mybucket.ata-test';
obj := 'test.xml';
region := 'eu-west-3';
service := 's3';
aws := 'amazonaws.com';
YYYYMMDD := FormatDateTime('yyyymmdd', now);
amzDate := FormatDateTime('yyyymmdd"T"hhnnss"Z"', TTimeZone.Local.ToUniversalTime(Now), TFormatSettings.Create('en-US'));
emptyHash := lowercase(SHA256HashAsHex(''));
host := Format('%s.%s.%s.%s', [bucket, service, region, aws]);
url := Format('%s://%s.%s.%s.%s/%s', ['https', bucket, service, region, aws, obj]);

// *** 1. Build the Canonical Request for Signature Version 4 ***
// HTTPRequestMethod
CanonicalRequest := URLEncodeValue('GET') +#10;
// CanonicalURI
CanonicalRequest := CanonicalRequest + '/' + URLEncodeValue(obj) +#10;
// CanonicalQueryString (empty just a newline)
CanonicalRequest := CanonicalRequest +#10;
// CanonicalHeaders
CanonicalRequest := CanonicalRequest + 'host:' + Trim(host) +#10
+ 'x-amz-content-sha256:' + emptyHash +#10
+ 'x-amz-date:' + Trim(amzDate) +#10;
// SignedHeaders
CanonicalRequest := CanonicalRequest + 'host;x-amz-content-sha256;x-amz-date' +#10;
// HexEncode(Hash(RequestPayload)) - (hash of an empty string)
CanonicalRequest := CanonicalRequest + emptyHash;

// *** 2. Create a String to Sign for Signature Version 4 ***
StringToSign := 'AWS4-HMAC-SHA256' +#10
+ amzDate +#10
+ UTF8String(YYYYMMDD) +'/'+ UTF8String(region) +'/'+ UTF8String(service) +UTF8String('/aws4_request') +#10
+ lowercase(SHA256HashAsHex(CanonicalRequest));

// *** 3. Calculate the Signature for AWS Signature Version 4 ***
DateKey := CalculateHMACSHA256(YYYYMMDD, 'AWS4' + SecretAccessKey);
DateRegionKey := CalculateHMACSHA256(region, DateKey);
DateRegionServiceKey := CalculateHMACSHA256(service, DateRegionKey);
SigningKey := CalculateHMACSHA256('aws4_request', DateRegionServiceKey);

Signature := lowercase(UrlEncodeValue(CalculateHMACSHA256(StringToSign, SigningKey)));

// *** 4. Create Authorisation Header and Add the Signature to the HTTP Request ***
AuthorisationHeader := 'AWS4-HMAC-SHA256 Credential='+AccessIdKey+'/'+YYYYMMDD+'/'+region+'/'+service+'/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature='+signature;
// (Gives <Code>InvalidRequest</Code> <Message>Missing required header for this request: x-amz-content-sha256</Message>)

// Have also tried
// AuthorisationHeader := 'Authorization: AWS4-HMAC-SHA256 Credential='+AccessIdKey+'/'+YYYYMMDD+'/'+region+'/'+service+'/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature='+signature;
// (Gives <Code>InvalidArgument</Code> <Message>Unsupported Authorization Type</Message>)

// *** 5. Add Header and Make Request ***
stm := TMemoryStream.Create;
try
try
Idhttp.Request.CustomHeaders.FoldLines := False;
Idhttp.Request.CustomHeaders.AddValue('Authorization', AuthorisationHeader);
Idhttp.Get(URL, stm);
except
on PE: EIdHTTPProtocolException do begin
s := PE.ErrorMessage;
Raise;
end;
on E: Exception do begin
s := E.Message;
Raise;
end;
end;

stm.Position := 0;
Memo1.Lines.LoadFromStream(stm);
finally
FreeAndNil(stm);
end;
end;

function SHA256HashAsHex(const value: string): String;
/// used for stringtosign
var
sha: TIdHashSHA256;
begin
LoadOpenSSLLibrary;
if not TIdHashSHA256.IsAvailable then
raise Exception.Create('SHA256 hashing is not available!');
sha := TIdHashSHA256.Create;
try
result := sha.HashStringAsHex(value, nil);
finally
sha.Free;
end;
end;

function CalculateHMACSHA256(const value, salt: String): String;
/// used for signingkey
var
hmac: TIdHMACSHA256;
hash: TIdBytes;
begin
LoadOpenSSLLibrary;
if not TIdHashSHA256.IsAvailable then
raise Exception.Create('SHA256 hashing is not available!');
hmac := TIdHMACSHA256.Create;
try
hmac.Key := IndyTextEncoding_UTF8.GetBytes(salt);
hash := hmac.HashValue(IndyTextEncoding_UTF8.GetBytes(value));
Result := EncodeBytes64(TArray<Byte>(hash));
finally
hmac.Free;
end;
end;

最佳答案

我在您的代码中注意到的几件事:

  • 创建 YYYYMMDD 时和 amzDate值,您正在调用 Now()两次,这会创建一个竞争条件,有可能导致这些变量代表不同的日期。不太可能,但可能。为避免这种情况,您应该调用 Now()仅 1 次并将结果保存到本地 TDateTime变量,然后在所有 FormatDateTime() 中使用该变量来电。

  • dtNow := Now();
    YYYYMMDD := FormatDateTime('yyyymmdd', dtNow);
    amzDate := FormatDateTime('yyyymmdd"T"hhnnss"Z"', TTimeZone.Local.ToUniversalTime(dtNow), TFormatSettings.Create('en-US'));
  • 使用 TIdHTTP 时的Request.CustomHeaders属性设置自定义Authorization header ,请确保您还设置了 Request.BasicAuthentication属性也为 False,否则为 TIdHTTP可以创建自己的Authorization: Basic ... header 使用其 Request.UsernameRequest.Password特性。你不想要两个 Authorization GET 中的标题要求。

  • Idhttp.Request.BasicAuthentication := False;
  • 您正在使用 x-amz-content-sha256x-amz-date授权计算中的 header ,但您没有将这些 header 添加到实际的 HTTP 请求中。 TIdHTTP将添加 Host header ,但您需要自己添加其他 header 。

  • Idhttp.Request.CustomHeaders.AddValue('x-amz-content-sha256', emptyHash);
    Idhttp.Request.CustomHeaders.AddValue('x-amz-date', amzDate);
  • 您的 SHA256HashAsHex()调用 Indy 的 TIdHashSHA256.HashStringAsHex() 时函数未指定字节编码方法(实际上,将编码显式设置为 nil 是不遗余力的)。因此,将使用 Indy 的默认字节编码,即 US-ASCII(除非您在 GIdDefaultTextEncoding 单元中将 Indy 的 IdGlobal 变量设置为其他值)。但是,您的 CalculateHMACSHA256()函数显式使用 UTF-8。您的 SHA256HashAsHex()函数应该使用 IndyTextEncoding_UTF8匹配:

  • result := sha.HashStringAsHex(value, IndyTextEncoding_UTF8);
  • CalculateHMACSHA256() 的输入盐和输出值需要是二进制字节,而不是字符串,当然也不是 base64 编码或十六进制编码的字符串。 Calculate the Signature for AWS Signature Version 4 中没有任何内容文档完全提到了base64的使用。

  • var
    DateKey, RegionKey, ServiceKey, SigningKey: TArray<Byte>;
    ...

    // *** 3. Calculate the Signature for AWS Signature Version 4 ***
    DateKey := CalculateHMACSHA256(YYYYMMDD, TEncoding.UTF8.GetBytes('AWS4' + SecretAccessKey));
    RegionKey := CalculateHMACSHA256(region, DateKey);
    ServiceKey := CalculateHMACSHA256(service, RegionKey);
    SigningKey := CalculateHMACSHA256('aws4_request', ServiceKey);

    Signature := CalculateHMACSHA256Hex(StringToSign, SigningKey);

    ...

    function CalculateHMACSHA256(const value: string; const salt: TArray<Byte>): TArray<Byte>;
    /// used for signingkey
    var
    hmac: TIdHMACSHA256;
    hash: TIdBytes;
    begin
    LoadOpenSSLLibrary;
    if not TIdHashSHA256.IsAvailable then
    raise Exception.Create('SHA256 hashing is not available!');
    hmac := TIdHMACSHA256.Create;
    try
    hmac.Key := TIdBytes(salt);
    hash := hmac.HashValue(IndyTextEncoding_UTF8.GetBytes(value));
    Result := TArray<Byte>(hash);
    finally
    hmac.Free;
    end;
    end;

    function CalculateHMACSHA256Hex(const value: string; const salt: TArray<Byte>): string;
    var
    hash: TArray<Byte>;
    begin
    hash := CalculateHMACSHA256(value, salt)
    Result := lowercase(ToHex(TIdBytes(hash)));
    end;

    关于amazon-s3 - 发出 S3 GET 请求时收到 400 Bad request,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61097561/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com