首页 > 解决方案 > AWS TranslateText REST API 调用添加签名 v4

问题描述

实际上,这不是一个问题,而是那些试图在没有 SDK 的情况下使用 AWS TranslateText 的人的答案。在获得运行版本之前,我遇到了很多问题。

在我看来,该服务的 AWS 文档并不完整,并且没有太多示例可供检查(至少对于 .Net)。

起初,我以为我没有生成正确的 V4 签名,但再次检查步骤和值后,我决定使用 Postman 调用服务。这很有帮助。

Postman 可以生成 AWS 签名!(是的,我不知道)所以最后我注意到签名值不是问题。

检查请求,我可以看到所需的标头及其一些值。

我的问题是我没有发送“x-amz-target”标头。偶然我发现此标头的值必须是“AWSShineFrontendService_20170701.TranslateText”(我在这里看到了类似的值https://rdrr.io/cran/aws.translate/src/R/translateHTTP.R

这个其他链接也很有帮助 Ivona 请求签名问题 - 签名不匹配(AWS 签名版本 4)

所以现在我有一个正在运行的版本,我想分享我的 .Net 代码。我希望它有帮助:) !!

using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web.Script.Serialization;

namespace AWS_TranslateTextTest
{
    class AWS_TranslateText
    {

        // Replace this with your own values
        const string AccessKey = "AKI_ADD_YOUR_ACCESSKEY";
        const string SecretKey = "ADD_YOUR_SECRETKEY";

        static void Main()
        {
                try
            {
                    string text = "Translate this text from English to German.";
                    string sourceLang = "en";
                    string targetLang = "de";

                    string responseText = TranslateText(text, sourceLang, targetLang);

                    JObject json = JObject.Parse(responseText);

                    string translatedText = ""; // to do read response  from json or responseText

                    if (json.ToString().Contains("TranslatedText")){
                        //To access to the properties in "dot" notation use a dynamic object
                        dynamic obj = json;
                        translatedText = obj.TranslatedText.Value;
                        Console.WriteLine("TranslatedText is: {0}", translatedText);
                    }
                    else{
                        Console.WriteLine("TranslatedText not found in response.");
                        throw new Exception(json.ToString());
                    }
            }
            catch (WebException ex)
            {
               Console.WriteLine(ex.Message);
                    if (ex.Response != null){
                foreach (string header in ex.Response.Headers)
                {
                    Console.WriteLine("{0}: {1}", header, ex.Response.Headers[header]);
                }
                using (var responseStream = ex.Response.GetResponseStream())
                {
                    if (responseStream != null)
                    {
                        using (var streamReader = new StreamReader(responseStream))
                        {
                            Console.WriteLine(streamReader.ReadToEnd());
                        }
                    }
                }    }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }           
        }


          private static string TranslateText(string text, string sourceLang, string targetLang)
        {
            var date = DateTime.UtcNow;

            const string algorithm = "AWS4-HMAC-SHA256";
            const string regionName = "eu-west-1";
            const string serviceName = "translate";
            const string method = "POST";
            const string canonicalUri = "/";    
            const string canonicalQueryString = "";  
            const string x_amz_target_header = "AWSShineFrontendService_20170701.TranslateText";

                const string contentType = "application/x-amz-json-1.1";


            const string host = serviceName + "." + regionName + ".amazonaws.com";

            var obj = new
            {
                     SourceLanguageCode = sourceLang,
                     TargetLanguageCode = targetLang,
                     Text = text
            };
            var requestPayload = new JavaScriptSerializer().Serialize(obj);

            var hashedRequestPayload = HexEncode(Hash(ToBytes(requestPayload)));

            var dateStamp = date.ToString("yyyyMMdd");
            var requestDate = date.ToString("yyyyMMddTHHmmss") + "Z";
            var credentialScope = string.Format("{0}/{1}/{2}/aws4_request", dateStamp, regionName, serviceName);

                var bytes = ToBytes(requestPayload);


                var headers = new SortedDictionary<string, string>
            {

                     {"content-length", bytes.Length.ToString()},
                     {"content-type", contentType},
                {"host", host},
                     {"x-amz-date", requestDate},
                     {"x-amz-target", x_amz_target_header}
            };

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

                string signedHeaders =
                string.Join(";", headers.Select(x => x.Key.ToLowerInvariant() ));

            // Task 1: Create a Canonical Request For Signature Version 4
            var canonicalRequest = method + '\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
            // StringToSign  = Algorithm + '\n' + RequestDate + '\n' + CredentialScope + '\n' + HashedCanonicalRequest

            var stringToSign = string.Format("{0}\n{1}\n{2}\n{3}", algorithm, requestDate, credentialScope,
                hashedCanonicalRequest);


            // Task 3: Calculate the AWS Signature Version 4

            // HMAC(HMAC(HMAC(HMAC("AWS4" + kSecret,"20130913"),"eu-west-1"),"tts"),"aws4_request")
            byte[] signingKey = GetSignatureKey(SecretKey, dateStamp, regionName, serviceName);

            // signature = HexEncode(HMAC(derived-signing-key, string-to-sign))
            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 =
                string.Format("{0} Credential={1}/{2}/{3}/{4}/aws4_request, SignedHeaders={5}, Signature={6}",
                    algorithm, AccessKey, dateStamp, regionName, serviceName, signedHeaders, signature);

            // Send the request
                string endpoint = "https://" + host; // + canonicalUri ;


            var webRequest = WebRequest.Create(endpoint);

            webRequest.Method = method;
            webRequest.Timeout = 20000;
            webRequest.ContentType = contentType;
            webRequest.Headers.Add("X-Amz-Date", requestDate);
            webRequest.Headers.Add("Authorization", authorization);
                webRequest.Headers.Add("X-Amz-Target", x_amz_target_header);    

            webRequest.ContentLength = bytes.Length; 

            using (Stream newStream = webRequest.GetRequestStream())
            {
                newStream.Write(bytes, 0, bytes.Length);
                newStream.Flush();
            }

            var response = (HttpWebResponse)webRequest.GetResponse();

            using (Stream responseStream = response.GetResponseStream())
            {
                if (responseStream != null)
                {

                          using (var streamReader = new StreamReader(responseStream))
                          {
                                string res = streamReader.ReadToEnd();
                                return res;
                          }
                }
            }



            return null;
        }

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

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

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

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

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

标签: c#restamazon-web-servicessignaturetranslate

解决方案


推荐阅读