1

我正在尝试创建一个简单的 C# 或 VB 程序来连接到 AWS API 并返回一个提供 EC2 实例数据的 JSON 字符串。相反,我收到错误“AWS 无法验证提供的访问凭证”

我尝试在PostmanVB 程序中连接,如下所示:

Imports System.Net
Imports System.IO

Module Main

    Sub Main()

        Dim result As String = RunQuery("", "https://ec2.amazonaws.com/?Action=DescribeInstances")

        Console.WriteLine(result)
        Console.ReadLine()

    End Sub


    Public Function RunQuery(creds As String, url As String, Optional data As String = Nothing) As String

        Dim request As HttpWebRequest = TryCast(WebRequest.Create(url), HttpWebRequest)
        request.ContentType = "application/json"
        request.Method = "GET"

        If data IsNot Nothing Then
            Using writer As StreamWriter = New StreamWriter(request.GetRequestStream())
                writer.Write(data)
            End Using
        End If

        Dim d As Date = Date.UtcNow
        Dim t As String = Format(d, "yyyyMMdd") & "T" & Format(d, "HHmmss") & "Z"

        request.Headers.Add("X-Amz-Date", t)
        request.Headers.Add("Authorization", "AWS4-HMAC-SHA256 Credential=***MYAPIKEY***/20201216/us-east-2/ec2/aws4_request, SignedHeaders=host;x-amz-date, Signature=***MYAPISECRET***")

        Dim response As HttpWebResponse = TryCast(request.GetResponse(), HttpWebResponse)
        Dim result As String = String.Empty

        Using reader As StreamReader = New StreamReader(response.GetResponseStream())
            result = reader.ReadToEnd()
        End Using

        Return result

    End Function
End Module

邮递员的输出是

<?xml version="1.0" encoding="UTF-8"?>
<Response><Errors><Error><Code>AuthFailure</Code><Message>AWS was not able to validate the provided access credentials</Message></Error></Errors><RequestID>37153d9f-c042-4ae3-8a13-06dc3b052afc</RequestID></Response>

邮递员输出

我根据邮递员代码编写了vb:

var client = new RestClient("https://ec2.amazonaws.com/?Action=DescribeInstances");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("X-Amz-Date", "20201216T092737Z");
request.AddHeader("Authorization", "AWS4-HMAC-SHA256 Credential=MYAPIKEY/20201216/us-east-2/ec2/aws4_request, SignedHeaders=host;x-amz-date, Signature=MYAPISIGNATURE");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);

类似的问题之前已经被问过很多次,但我能看到的唯一答案是 PC 时钟是错误的,所以我已经更正了我的问题,使其在真实互联网时间的 0.2 秒内。

我也找不到另一个同样简单的 VB 或 C# 程序成功连接到 EC2,所以这个线程将来可能会帮助其他人。

我想知道签名是否必须用 base64 或其他算法编码?否则我怎样才能让这个在邮递员或 vb/c# 中工作?TIA

4

1 回答 1

2

所以有几个问题

  1. 错误的网址,正确的是https://ec2.us-east-2.amazonaws.com/?Action=DescribeInstances&Version=2016-11-15

  2. 我认为秘密是签名,但事实并非如此。

这是一个成功连接到 AWS 的完整 VB.NET 控制台应用程序。抱歉,它没有评论,但代码相当明显且不会太长。


Option Explicit On
Option Compare Text
Option Strict On

Imports System.Net.Http
Imports System.Security.Cryptography
Imports System.Text

Public Module Program

    Public Sub Main(ByVal args As String())

        If args.Length <> 5 Then
            Throw New Exception("AWS Integration requires 5 parameters: url accessKey secretKey awsRegion awsServiceName")
        End If

        Dim url As String = args(0)             '   "https://ec2.us-east-2.amazonaws.com/?Action=DescribeInstances&Version=2016-11-15"
        Dim accessKey As String = args(1)       ' api key
        Dim secretkey As String = args(2)       ' api secret
        Dim awsRegion As String = args(3)       ' = "us-east-2"
        Dim awsServiceName As String = args(4)  '= "ec2"

        Dim msg As HttpRequestMessage = New HttpRequestMessage(HttpMethod.[Get], url)
        msg.Headers.Host = msg.RequestUri.Host
        Dim utcNowSaved As DateTimeOffset = DateTimeOffset.UtcNow
        Dim amzLongDate As String = utcNowSaved.ToString("yyyyMMddTHHmmssZ")
        Dim amzShortDate As String = utcNowSaved.ToString("yyyyMMdd")
        msg.Headers.Add("x-amz-date", amzLongDate)
        Dim canonicalRequest As New StringBuilder
        canonicalRequest.Append(msg.Method.ToString & vbLf)
        canonicalRequest.Append(String.Join("/", msg.RequestUri.AbsolutePath.Split("/"c).Select(AddressOf Uri.EscapeDataString)) & vbLf)
        canonicalRequest.Append(GetCanonicalQueryParams(msg) & vbLf)
        Dim headersToBeSigned As New List(Of String)

        For Each header In msg.Headers.OrderBy(Function(a) a.Key.ToLowerInvariant, StringComparer.OrdinalIgnoreCase)
            canonicalRequest.Append(header.Key.ToLowerInvariant)
            canonicalRequest.Append(":")
            canonicalRequest.Append(String.Join(",", header.Value.[Select](Function(s) s.Trim)))
            canonicalRequest.Append(vbLf)
            headersToBeSigned.Add(header.Key.ToLowerInvariant)
        Next

        canonicalRequest.Append(vbLf)
        Dim signedHeaders As String = String.Join(";", headersToBeSigned)
        canonicalRequest.Append(signedHeaders & vbLf)
        canonicalRequest.Append("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
        Dim stringToSign As String = "AWS4-HMAC-SHA256" & vbLf & amzLongDate & vbLf & amzShortDate & "/" & awsRegion & "/" & awsServiceName & "/aws4_request" & vbLf & Hash(Encoding.UTF8.GetBytes(canonicalRequest.ToString))
        Dim dateKey() As Byte = HmacSha256(Encoding.UTF8.GetBytes("AWS4" & secretkey), amzShortDate)
        Dim dateRegionKey() As Byte = HmacSha256(dateKey, awsRegion)
        Dim dateRegionServiceKey() As Byte = HmacSha256(dateRegionKey, awsServiceName)
        Dim signingKey() As Byte = HmacSha256(dateRegionServiceKey, "aws4_request")
        Dim signature As String = ToHexString(HmacSha256(signingKey, stringToSign.ToString))
        Dim credentialScope As String = amzShortDate & "/" & awsRegion & "/" & awsServiceName & "/aws4_request"
        msg.Headers.TryAddWithoutValidation("Authorization", "AWS4-HMAC-SHA256 Credential=" & accessKey & "/" & credentialScope & ", SignedHeaders=" & signedHeaders & ", Signature=" & signature)
        Dim client As HttpClient = New HttpClient
        Dim result As HttpResponseMessage = client.SendAsync(msg).Result

        If result.IsSuccessStatusCode Then
            'Console.WriteLine(result.Headers)
            Dim s As String = result.Content.ReadAsStringAsync().Result
            Console.WriteLine(s)
        Else
            Console.WriteLine(result.StatusCode & vbCrLf & result.ToString.Replace(vbCr, "").Replace(vbLf, ""))
        End If

    End Sub

    Private Function GetCanonicalQueryParams(ByVal request As HttpRequestMessage) As String
        Dim values = New SortedDictionary(Of String, String)
        Dim querystring = System.Web.HttpUtility.ParseQueryString(request.RequestUri.Query)

        For Each key In querystring.AllKeys

            If key Is Nothing Then
                values.Add(Uri.EscapeDataString(querystring(key)), $"{Uri.EscapeDataString(querystring(key))}=")
            Else
                values.Add(Uri.EscapeDataString(key), $"{Uri.EscapeDataString(key)}={Uri.EscapeDataString(querystring(key))}")
            End If
        Next

        Dim queryParams = values.Select(Function(a) a.Value)
        Return String.Join("&", queryParams)
    End Function

    Public Function Hash(ByVal bytesToHash() As Byte) As String
        Return ToHexString(SHA256.Create.ComputeHash(bytesToHash))
    End Function

    Private Function ToHexString(ByVal array As IReadOnlyCollection(Of Byte)) As String
        Dim hex = New StringBuilder(array.Count * 2)

        For Each b In array
            hex.AppendFormat("{0:x2}", b)
        Next

        Return hex.ToString
    End Function

    Private Function HmacSha256(ByVal key() As Byte, ByVal data As String) As Byte()
        Return New HMACSHA256(key).ComputeHash(Encoding.UTF8.GetBytes(data))
    End Function
End Module


HTH某人:)

于 2020-12-17T12:04:27.333 回答