- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试向 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.Username
和 Request.Password
特性。你不想要两个 Authorization
GET
中的标题要求。 Idhttp.Request.BasicAuthentication := False;
x-amz-content-sha256
和 x-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/
这个问题在这里已经有了答案: Why don't Java's +=, -=, *=, /= compound assignment operators require casting? (11 个
我搜索了很多,但没有一个链接能帮助我解决这个问题。我得到了 ORA-21500: internal error code, arguments: [%s], [%s], [%s], [%s], [%s
我正在做 RegexOne 正则表达式教程,它有一个 question关于编写正则表达式以删除不必要的空格。 教程中提供的解决方案是 We can just skip all the starting
([\s\S]+|\s?) 中 |\s? 的目的或作用是什么?如果没有它,表达式会不会与 ([\s\S]+) 相同? 最佳答案 这不是完全相同的。 ([\s\S]+|\s?) 会匹配空字符串,而 ([
这个正则表达式有一组还是两组? 我正在尝试使用第二组访问 bookTitle 但出现错误: Pattern pattern = Pattern.compile("^\\s*(.*?)\\s+-\\s+
在 C 中给定一个字符串指针 s,下面的迭代会做什么?即它以什么方式遍历字符串? for (++s ; *s; ++s); 最佳答案 for (++s ; *s;++s) 表示 将指针 s 递增到字符
我正在用一个 node.js 应用程序解析一个大列表并有这段代码 sizeCode = dbfr.CN_DESC.split('\s+-\s*|\s*-\s+') 这似乎不起作用,因为它返回了 [ '
我正在编写一个简单的字符串连接程序。 该程序按照我发布的方式运行。但是,我首先使用以下代码编写它来查找字符串的结尾: while (*s++) ; 但是,这个方法并没有奏效。我传递给它的字符串
这个问题已经有答案了: What does (?和aramchand来自Mohandas Karamchand G 因此,在使用这些匹配来分割字符串后,您最终会得到 {"M", "K", "G"} 注
我正在尝试转换 Map到 List使用 lambda。 本质上,我想将键和值与 '=' 连接起来之间。这看起来微不足道,但我找不到如何去做。 例如 Map map = new HashMap<>();
我正在经历 K & R,并且在递增指针时遇到困难。练习 5.3(第 107 页)要求您使用指针编写一个 strcat 函数。 在伪代码中,该函数执行以下操作: 将 2 个字符串作为输入。 找到字符串
在下面的代码中,pS 和 s.pS 在最后一行是否保证相等?也就是说,在语句S s = S();中,是否可以确定不会构造一个临时的S? #include using namespace std; s
演示示例代码: public void ReverseString(char[] s) { for(int i = 0, j = s.Length-1; i < j; i++, j--){
我一直在寻找类似于 .NET examples 中的示例的 PowerShell 脚本.取一个 New-TimeSpan 并显示为 1 天 2 小时 3 分钟 4 秒。排除其零的地方,在需要的地方添加
def func(s): s = s + " is corrected" return s string_list = ["She", "He"] for s in string_li
我是 python 的新手。当我在互联网上搜索 lambda 时。我在 lambda_functions 中找到了这个声明. processFunc = collapse and (lambda s:
我最近开始学习正则表达式,并试图为上面的问题写一个正则表达式。如果限制只放在一个字母上(例如不超过 2 个“b”),这并不困难。 那么答案就是:a* c*(b|ε)a* c*(b|ε)a* c* 但是
当我运行 npm install 时出现以下错误,但我无法修复它。 我试过:npm install -g windows-build-tools 也没有修复这个错误 ERR! configure
有很多有趣的haskell网上可以找到片段。 This post可以在 this (awesome) Stack Overflow question 下找到. The author写道: discou
我知道以下三行代码旨在将字符串提取到$ value中并将其存储在$ header中。但是我不知道$value =~ s/^\s+//;和$value =~ s/\s+$//;之间有什么区别。 $val
我是一名优秀的程序员,十分优秀!