当前位置: 首页 > 知识库问答 >
问题:

在C中调用AWSAPI网关URL#

蒙光华
2023-03-14

我们正在尝试从C#Windows服务调用AWS API网关来执行后台作业。哪个应该触发API网关定期初始化请求?

我们使用RestSharp调用APIendpoint,该类称为AwsAuthenticator,它继承自RestSharp.Authenticators.IAAuthenticator。但当我们调用API Gateway时,收到的错误是“我们计算的请求签名与您提供的签名不匹配。请检查您的AWS秘密访问密钥和签名方法。有关详细信息,请参阅服务文档。”

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;

namespace ConsoleApp3
{
public class AwsAuthenticator : RestSharp.Authenticators.IAuthenticator
{
    public string AccessKeyId { get; }
    public string AccessKeySecret { get; }
    public string Region { get; }

    public AwsAuthenticator(string accessKeyId, string accessKeySecret, string region)
    {
        AccessKeyId = accessKeyId;
        AccessKeySecret = accessKeySecret;
        Region = region;
    }

    private static HashSet<string> ignoredHeaders = new HashSet<string>() {
        "authorization",
        "content-length",
        "content-type",
        "user-agent"
    };

    public void Authenticate(RestSharp.IRestClient client, RestSharp.IRestRequest request)
    {
        DateTime signingDate = DateTime.UtcNow;
        SetContentMd5(request);
        SetContentSha256(request);
        SetHostHeader(request, client);
        SetDateHeader(request, signingDate);
        SortedDictionary<string, string> headersToSign = GetHeadersToSign(request);
        string signedHeaders = GetSignedHeaders(headersToSign);
        string canonicalRequest = GetCanonicalRequest(client, request, headersToSign);
        byte[] canonicalRequestBytes = System.Text.Encoding.UTF8.GetBytes(canonicalRequest);
        string canonicalRequestHash = BytesToHex(ComputeSha256(canonicalRequestBytes));
        string stringToSign = GetStringToSign(Region, signingDate, canonicalRequestHash);
        byte[] signingKey = GenerateSigningKey(Region, signingDate);

        byte[] stringToSignBytes = System.Text.Encoding.UTF8.GetBytes(stringToSign);

        byte[] signatureBytes = SignHmac(signingKey, stringToSignBytes);

        string signature = BytesToHex(signatureBytes);

        string authorization = GetAuthorizationHeader(signedHeaders, signature, signingDate, Region);
        request.AddHeader("Authorization", authorization);

    }

    public string GetCredentialString(DateTime signingDate, string region)
    {
        return AccessKeyId + "/" + GetScope(region, signingDate);
    }

    private string GetAuthorizationHeader(string signedHeaders, string signature, DateTime signingDate, string region)
    {
        return "AWS4-HMAC-SHA256 Credential=" + this.AccessKeyId + "/" + GetScope(region, signingDate) +
            ", SignedHeaders=" + signedHeaders + ", Signature=" + signature;
    }

    private string GetSignedHeaders(SortedDictionary<string, string> headersToSign)
    {
        return string.Join(";", headersToSign.Keys);
    }

    private byte[] GenerateSigningKey(string region, DateTime signingDate)
    {
        byte[] formattedDateBytes = System.Text.Encoding.UTF8.GetBytes(signingDate.ToString("yyyMMdd"));
        byte[] formattedKeyBytes = System.Text.Encoding.UTF8.GetBytes("AWS4" + this.AccessKeySecret);
        byte[] dateKey = SignHmac(formattedKeyBytes, formattedDateBytes);

        byte[] regionBytes = System.Text.Encoding.UTF8.GetBytes(region);
        byte[] dateRegionKey = SignHmac(dateKey, regionBytes);

        byte[] serviceBytes = System.Text.Encoding.UTF8.GetBytes("execute-api");
        byte[] dateRegionServiceKey = SignHmac(dateRegionKey, serviceBytes);

        byte[] requestBytes = System.Text.Encoding.UTF8.GetBytes("aws4_request");
        return SignHmac(dateRegionServiceKey, requestBytes);
    }

    private byte[] SignHmac(byte[] key, byte[] content)
    {
        HMACSHA256 hmac = new HMACSHA256(key);
        hmac.Initialize();
        return hmac.ComputeHash(content);
    }

    private string GetStringToSign(string region, DateTime signingDate, string canonicalRequestHash)
    {
        return "AWS4-HMAC-SHA256\n" +
            signingDate.ToString("yyyyMMddTHHmmssZ") + "\n" +
            GetScope(region, signingDate) + "\n" +
            canonicalRequestHash;
    }

    private string GetScope(string region, DateTime signingDate)
    {
        string formattedDate = signingDate.ToString("yyyyMMdd");
        return formattedDate + "/" + region + "/execute-api/aws4_request";
    }

    private byte[] ComputeSha256(byte[] body)
    {

        SHA256 sha256 = SHA256.Create();
        return sha256.ComputeHash(body);
    }

    private string BytesToHex(byte[] checkSum)
    {
        return BitConverter.ToString(checkSum).Replace("-", string.Empty).ToLower();
    }

    public string PresignPostSignature(string region, DateTime signingDate, string policyBase64)
    {
        byte[] signingKey = this.GenerateSigningKey(region, signingDate);
        byte[] stringToSignBytes = System.Text.Encoding.UTF8.GetBytes(policyBase64);

        byte[] signatureBytes = SignHmac(signingKey, stringToSignBytes);
        string signature = BytesToHex(signatureBytes);

        return signature;
    }

    public string PresignURL(RestSharp.IRestClient client, RestSharp.IRestRequest request, int expires)
    {
        DateTime signingDate = DateTime.UtcNow;
        string requestQuery = "";
        string path = request.Resource;

        requestQuery = "X-Amz-Alhtml" target="_blank">gorithm=AWS4-HMAC-SHA256&";
        requestQuery += "X-Amz-Credential="
            + this.AccessKeyId
            + Uri.EscapeDataString("/" + GetScope(Region, signingDate))
            + "&";
        requestQuery += "X-Amz-Date="
            + signingDate.ToString("yyyyMMddTHHmmssZ")
            + "&";
        requestQuery += "X-Amz-Expires="
            + expires
            + "&";
        requestQuery += "X-Amz-SignedHeaders=host";

        string canonicalRequest = GetPresignCanonicalRequest(client, request, requestQuery);
        byte[] canonicalRequestBytes = System.Text.Encoding.UTF8.GetBytes(canonicalRequest);
        string canonicalRequestHash = BytesToHex(ComputeSha256(canonicalRequestBytes));
        string stringToSign = GetStringToSign(Region, signingDate, canonicalRequestHash);
        byte[] signingKey = GenerateSigningKey(Region, signingDate);
        byte[] stringToSignBytes = System.Text.Encoding.UTF8.GetBytes(stringToSign);
        byte[] signatureBytes = SignHmac(signingKey, stringToSignBytes);
        string signature = BytesToHex(signatureBytes);

        // Return presigned url.
        return client.BaseUrl + path + "?" + requestQuery + "&X-Amz-Signature=" + signature;
    }

    private string GetPresignCanonicalRequest(RestSharp.IRestClient client, RestSharp.IRestRequest request, string requestQuery)
    {
        LinkedList<string> canonicalStringList = new LinkedList<string>();
        canonicalStringList.AddLast(request.Method.ToString());

        string path = request.Resource;
        if (!path.StartsWith("/"))
        {
            path = "/" + path;
        }
        canonicalStringList.AddLast(path);
        canonicalStringList.AddLast(requestQuery);
        canonicalStringList.AddLast("host:" + client.BaseUrl.Host);
        canonicalStringList.AddLast("");
        canonicalStringList.AddLast("host");
        canonicalStringList.AddLast("UNSIGNED-PAYLOAD");

        return string.Join("\n", canonicalStringList);
    }

    private string GetCanonicalRequest(RestSharp.IRestClient client, RestSharp.IRestRequest request,
        SortedDictionary<string, string> headersToSign)
    {
        LinkedList<string> canonicalStringList = new LinkedList<string>();
        canonicalStringList.AddLast(request.Method.ToString());

        string[] path = request.Resource.Split(new char[] { '?' }, 2);
        if (!path[0].StartsWith("/"))
        {
            path[0] = "/" + path[0];
        }
        canonicalStringList.AddLast(path[0]);

        string query = "";
        if (path.Length == 2)
        {
            var parameterString = path[1];
            var parameterList = parameterString.Split('&');
            SortedSet<string> sortedQueries = new SortedSet<string>();
            foreach (string individualParameterString in parameterList)
            {
                if (individualParameterString.Contains('='))
                {
                    string[] splitQuery = individualParameterString.Split(new char[] { '=' }, 2);
                    sortedQueries.Add(splitQuery[0] + "=" + splitQuery[1]);
                }
                else
                {
                    sortedQueries.Add(individualParameterString + "=");
                }
            }
            query = string.Join("&", sortedQueries);
        }
        canonicalStringList.AddLast(query);

        foreach (string header in headersToSign.Keys)
        {
            canonicalStringList.AddLast(header + ":" + headersToSign[header]);
        }
        canonicalStringList.AddLast("");
        canonicalStringList.AddLast(string.Join(";", headersToSign.Keys));
        if (headersToSign.Keys.Contains("x-amz-content-sha256"))
        {
            canonicalStringList.AddLast(headersToSign["x-amz-content-sha256"]);
        }
        else
        {
            canonicalStringList.AddLast("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
        }

        return string.Join("\n", canonicalStringList);
    }

    private SortedDictionary<string, string> GetHeadersToSign(RestSharp.IRestRequest request)
    {
        var headers = request.Parameters.Where(p => p.Type.Equals(RestSharp.ParameterType.HttpHeader)).ToList();

        SortedDictionary<string, string> sortedHeaders = new SortedDictionary<string, string>();
        foreach (var header in headers)
        {
            string headerName = header.Name.ToLower();
            string headerValue = header.Value.ToString();
            if (!ignoredHeaders.Contains(headerName))
            {
                sortedHeaders.Add(headerName, headerValue);
            }
        }
        return sortedHeaders;
    }

    private void SetDateHeader(RestSharp.IRestRequest request, DateTime signingDate)
    {
        request.AddHeader("x-amz-date", signingDate.ToString("yyyyMMddTHHmmssZ"));
    }

    private void SetHostHeader(RestSharp.IRestRequest request, RestSharp.IRestClient client)
    {
        request.AddHeader("Host", client.BaseUrl.Host + (client.BaseUrl.Port != 80 ? ":" + client.BaseUrl.Port : string.Empty));
    }

    private void SetContentSha256(RestSharp.IRestRequest request)
    {
        if (request.Method == RestSharp.Method.PUT || request.Method.Equals(RestSharp.Method.POST))
        {
            var bodyParameter = request.Parameters.Where(p => p.Type.Equals(RestSharp.ParameterType.RequestBody)).FirstOrDefault();
            if (bodyParameter == null)
            {
                request.AddHeader("x-amz-content-sha256", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
                return;
            }
            byte[] body = null;
            if (bodyParameter.Value is string)
            {
                body = System.Text.Encoding.UTF8.GetBytes(bodyParameter.Value as string);
            }
            if (bodyParameter.Value is byte[])
            {
                body = bodyParameter.Value as byte[];
            }
            if (body == null)
            {
                body = new byte[0];
            }
            SHA256 sha256 = System.Security.Cryptography.SHA256.Create();
            byte[] hash = sha256.ComputeHash(body);
            string hex = BitConverter.ToString(hash).Replace("-", string.Empty).ToLower();
            request.AddHeader("x-amz-content-sha256", hex);
        }
        else
        {
            request.AddHeader("x-amz-content-sha256", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
        }
    }

    private void SetContentMd5(RestSharp.IRestRequest request)
    {
        if (request.Method == RestSharp.Method.PUT || request.Method.Equals(RestSharp.Method.POST))
        {
            var bodyParameter = request.Parameters.Where(p => p.Type.Equals(RestSharp.ParameterType.RequestBody)).FirstOrDefault();
            if (bodyParameter == null)
            {
                return;
            }
            byte[] body = null;
            if (bodyParameter.Value is string)
            {
                body = System.Text.Encoding.UTF8.GetBytes(bodyParameter.Value as string);
            }
            if (bodyParameter.Value is byte[])
            {
                body = bodyParameter.Value as byte[];
            }
            if (body == null)
            {
                body = new byte[0];
            }
            MD5 md5 = MD5.Create();
            byte[] hash = md5.ComputeHash(body);

            string base64 = Convert.ToBase64String(hash);
            request.AddHeader("Content-MD5", base64);
        }
    }
}
////////////////////////
public class MainClass
    {
    public void Execute()
        {
            var client = new RestClient("https://nm47849kod.execute-api.ap-southeast1.amazonaws.com/samplegateway/");
            var request = new RestRequest("/", Method.POST);
            var postData = new { Mode = 4 };
            request.AddParameter("application/json",JsonConvert.SerializeObject(postData),ParameterType.RequestBody); AwsAuthenticator awsAuthenticator = new AwsAuthenticator("AccessKeyXXXXX", "SECKEYxxxx12313123123123123", "apsoutheast-1");
            awsAuthenticator.Authenticate(client,request);

            IRestResponse response = client.Execute(request);
            var content = response.Content; // raw content as string
            Console.WriteLine(content);
            Console.ReadLine();
        }
}

错误详情:

{“信息”:"我们计算的请求签名与您提供的签名不匹配。请检查您的AWS秘密访问密钥和签名方法。有关详细信息,请参阅服务文档。\n\n此请求的规范字符串应该是\n'POST\n/samplegateway/\n\n content-md5:rkT7BbUvFInBgrPCuA0UZw==\n host:nm47849kod.execute-api.ap-southest-1.amazonaws.com\nx-amz-content-sha256:0318f62547c9078687e73f987ec26fa557047b67f54bb99b8047c950990ae42c\nx-amz日期:20190601T102835Z\n\n内容-md5;主机;x-amz-content-sha256;x-amz-date\n0318F625447C90787E73F987EC26FA55704767F54BB99B8047C950990AE42C'\n\n\n\n要签名的字符串应该是\n'AWZ-amz-exe-T18080808080808080765Z\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n可爱的api/aws4_请求\n8f89bd5010655fb26a8de5e29d48d6129ac7875e5eb6bc2faeb8e41864b4d49e'\n“}。

共有1个答案

易研
2023-03-14

我们发现了问题。

下面是工作代码,这解决了我的问题。我分享这一点是为了让团体受益。上面的类是完全重写的,当调用它时可以工作。

public class ApiRequest
{
    private const string ServiceName = "execute-api";
    private const string Algorithm = "AWS4-HMAC-SHA256";
    private const string ContentType = "application/json";
    private const string SignedHeaders = "content-type;host;x-amz-date;x-api-key";
    private const string DateTimeFormat = "yyyyMMddTHHmmssZ";
    private const string DateFormat = "yyyyMMdd";

    public AwsApiGatewayRequest AwsApiGatewayRequest;

    public ApiRequest(AwsApiGatewayRequest request)
    {
        AwsApiGatewayRequest = request;

        if (string.IsNullOrEmpty(AwsApiGatewayRequest.RequestMethod))
            AwsApiGatewayRequest.RequestMethod = "POST";

        if (string.IsNullOrEmpty(AwsApiGatewayRequest.xApiKey))
            AwsApiGatewayRequest.xApiKey = "";
    }

    public WebResponse GetPostResponse()
    {
        var request = GetPostRequest();
        return request.GetResponse();
    }

    public WebRequest GetPostRequest()
    {
        string hashedRequestPayload = CreateRequestPayload(AwsApiGatewayRequest.JsonData);

        string authorization = Sign(hashedRequestPayload, AwsApiGatewayRequest.RequestMethod, AwsApiGatewayRequest.AbsolutePath, AwsApiGatewayRequest.QueryString);
        string requestDate = DateTime.UtcNow.ToString(DateTimeFormat);

        var webRequest = WebRequest.Create($"https://{AwsApiGatewayRequest.Host}{AwsApiGatewayRequest.AbsolutePath}");

        webRequest.Timeout = AwsApiGatewayRequest.RequestTimeout.HasValue ? AwsApiGatewayRequest.RequestTimeout.Value : 50000;
        webRequest.Method = AwsApiGatewayRequest.RequestMethod;
        webRequest.ContentType = ContentType;
        webRequest.Headers.Add("X-Amz-date", requestDate);
        webRequest.Headers.Add("Authorization", authorization);
        webRequest.Headers.Add("x-amz-content-sha256", hashedRequestPayload);

        if (!string.IsNullOrEmpty(AwsApiGatewayRequest.AdditionalHeaders))
        {
            // parse apart and apply the additional headers
            string[] headers = AwsApiGatewayRequest.AdditionalHeaders.Split(';');
            foreach (string header in headers)
            {
                var headervalue = header.Split('=');
                if (headervalue.Count() == 2)
                    webRequest.Headers.Add(headervalue[0], headervalue[1]);
            }
        }

        if (!string.IsNullOrEmpty(AwsApiGatewayRequest.SessionToken))
            webRequest.Headers.Add("X-Amz-Security-Token", AwsApiGatewayRequest.SessionToken);
        webRequest.ContentLength = AwsApiGatewayRequest.JsonData.Length;

        var encoding = new ASCIIEncoding();
        var data = encoding.GetBytes(AwsApiGatewayRequest.JsonData);

        using (var newStream = webRequest.GetRequestStream())
        {
            newStream.Write(data, 0, data.Length);
            newStream.Close();
        }

        return webRequest;
    }

    private string CreateRequestPayload(string jsonString)
    {
        return HexEncode(Hash(ToBytes(jsonString)));
    }

    private string Sign(string hashedRequestPayload, string requestMethod, string canonicalUri, string canonicalQueryString)
    {
        var currentDateTime = DateTime.UtcNow;

        var dateStamp = currentDateTime.ToString(DateFormat);
        var requestDate = currentDateTime.ToString(DateTimeFormat);
        var credentialScope = $"{dateStamp}/{AwsApiGatewayRequest.RegionName}/{ServiceName}/aws4_request";

        var headers = new SortedDictionary<string, string> {
            { "content-type", ContentType },
            { "host", AwsApiGatewayRequest.Host },
            { "x-amz-date", requestDate },
            { "x-api-key", AwsApiGatewayRequest.xApiKey }
        };

        var canonicalHeaders = string.Join("\n", headers.Select(x => x.Key.ToLowerInvariant() + ":" + x.Value.Trim())) + "\n";

        // Task 1: Create a Canonical Request For Signature Version 4
        var canonicalRequest = $"{requestMethod}\n{canonicalUri}\n{canonicalQueryString}\n{canonicalHeaders}\n{SignedHeaders}\n{hashedRequestPayload}";
        var hashedCanonicalRequest = HexEncode(Hash(ToBytes(canonicalRequest)));

        // Task 2: Create a String to Sign for Signature Version 4
        var stringToSign = $"{Algorithm}\n{requestDate}\n{credentialScope}\n{hashedCanonicalRequest}";

        // Task 3: Calculate the AWS Signature Version 4
        var signingKey = GetSignatureKey(AwsApiGatewayRequest.SecretKey, dateStamp, AwsApiGatewayRequest.RegionName, ServiceName);
        var signature = HexEncode(HmacSha256(stringToSign, signingKey));

        // Task 4: Prepare a signed request
        // Authorization: algorithm Credential=access key ID/credential scope, SignedHeadaers=SignedHeaders, Signature=signature
        var authorization = $"{Algorithm} Credential={AwsApiGatewayRequest.AccessKey}/{dateStamp}/{AwsApiGatewayRequest.RegionName}/{ServiceName}/aws4_request, SignedHeaders={SignedHeaders}, Signature={signature}";

        return authorization;
    }

    private byte[] GetSignatureKey(string key, string dateStamp, string regionName, string serviceName)
    {
        var kDate = HmacSha256(dateStamp, ToBytes("AWS4" + key));
        var kRegion = HmacSha256(regionName, kDate);
        var kService = HmacSha256(serviceName, kRegion);
        return HmacSha256("aws4_request", kService);
    }

    private byte[] ToBytes(string str)
    {
        return Encoding.UTF8.GetBytes(str.ToCharArray());
    }

    private string HexEncode(byte[] bytes)
    {
        return BitConverter.ToString(bytes).Replace("-", string.Empty).ToLowerInvariant();
    }

    private byte[] Hash(byte[] bytes)
    {
        return SHA256.Create().ComputeHash(bytes);
    }

    private byte[] HmacSha256(string data, byte[] key)
    {
        return new HMACSHA256(key).ComputeHash(ToBytes(data));
    }
}

执行参数

var request = new AwsApiGatewayRequest()
       {
           RegionName = "",
           Host = ,
           AccessKey = "",
           SecretKey = "",
           RequestMethod = "POST",
           AbsolutePath = ,
           JsonData = "{\"Mode\":\"4\"}",
           SessionToken = ""
       };//Invoke this using RestClient...

这里的问题是我们未能添加AWS所需的附加标头。在这个版本中,我们添加了因此它纠正。

谢谢你的支持。

 类似资料:
  • 问题内容: 我有很多用C语言编写的函数,我希望我用Python编写的一些代码能够访问这些函数。 我已经在这里阅读了几个类似问题的问题,但是我对我应该采用哪种方法感到困惑。 一个问题建议使用ctypes,另一个问题建议使用cython。我已经阅读了两者的文档,但对于哪个对我来说更好,我还是一无所知。 基本上,我已经编写了一些Python代码来进行二维FFT,我希望C代码能够看到该结果,然后通过编写的

  • 我的(非常简单的)代码看起来是这样的: 我将构建好的jar上传到Lambda,并通过创建一个测试事件在AWS控制台中进行测试。它返回正确的响应,正如预期的那样。 但是,我希望API网关调用这个Lambda。我既使用了Lambda代理集成,也定义了自己的主体映射模板。我似乎无法使它工作,我一直得到: 我确定这是一件很简单的事情,我只是错过了一些东西...

  • 我正在使用Chrome浏览器版本57.0.2987.110,我正在尝试用Selenium打开一个网页,只是它只打开了标准网站的浏览器,而没有打开我告诉它的网页。这不是一个选择改变浏览器,我必须工作与Chrome。 这里有一个错误:

  • 问题内容: 所以我在C中使用了Java本机接口的以下代码,但是我想将其转换为C ++,但不确定如何。 Java程序: JNI与C和C ++交互的区别是什么?任何帮助是极大的赞赏。 谢谢,皮特 问题答案: 我曾经有一本书《EssentialJNI》。虽然它有些过时,但今天仍然可以使用。 如果我没记错的话,在C语言中,Java构造只是指针。因此,在您的代码中,“ ”是取消引用指针,以使您可以访问基础方

  • 我创建了一些Lambda函数,并使用SAM进行了部署。部署是成功的,但当试图到达endpoint时,我总是获得 即使我使用头发送正确的承载令牌。然后,如果我去Authorizer并运行测试,它会很好地通过并在CloudWatch中生成日志,但是当我从前端应用程序或REST客户端应用程序运行到endpoint的请求时,我会得到未经授权的消息,并且检查CloudWatch,就不会执行Authorize

  • 问题内容: 有谁知道C / C ++代码来查找可用的网络接口? 我一直在寻找一些代码,但是大多数时候它们都很复杂。有没有简单的方法可以做到这一点? 问题答案: 请参见getifaddrs手册页。最后有一个示例程序。