Create the Encryption Input - the String to Sign
The topic Making Query Requests specifies the explicit protocol for creating the string to sign. This post example implements the procedure in C#. The first step is to sort the query elements by parameter name in natural byte ordering. Most code languages include a list class that orders members by a comparator suited to the type of the list members. This example uses the .NET generic class, SortedDictionary
>>> The following text is for search engines; the formatting is garbled. Please use the following link to view code with the text of this example.
See http://integral-design.s3.amazonaws.com/Signature-CSharp-Amazon-EC2-Authentication.htm
<<<
class ParamComparer : IComparer<string>
{
public int Compare(string p1, string p2)
{
return string.CompareOrdinal(p1, p2);
}
}
The following code segment creates a SortedDictionary object and adds request parameters for the EC2 action DescribeInstances. Using the ParamComparer, the SortedDictionary automatically orders the members in UTF-8 natural byte ordering.
ParamComparer pc = new ParamComparer();
SortedDictionary<string, string> sortedRequestPars = new SortedDictionary<string, string>(pc);
string action = "DescribeInstances";
sortedRequestPars.Add("Action", action);
sortedRequestPars.Add("SignatureMethod", "HmacSHA1");
sortedRequestPars.Add("Version", "2010-11-15");
sortedRequestPars.Add("SignatureVersion", "2");
string date = GetEC2Date();
sortedRequestPars.Add("Timestamp", date);
sortedRequestPars.Add("AWSAccessKeyId", accessKeyId);
The Timestamp parameter that the previous code initializes must be formatted in Coordinated Universal Time (abbreviated UTC). The static method of the .NET DateTime class gets the request time and formats it according to the EC2 specification. You can use the Timestamp parameter or the Expires parameter in an EC2 request. A request that uses the Timestamp parameter is valid until fifteen minutes after the specified time. A request that uses the Expires parameter is valid until the time specified.
static public string GetEC2Date()
{
//string httpDate = DateTime.UtcNow.ToString("s") + "Z";
string httpDate =
DateTime.UtcNow.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'", DateTimeFormatInfo.InvariantInfo);
return httpDate;
}
The parameters and their values must be URL encoded. This is easier to do in some programming languages than in others. This example uses C# and shows explicit URL encoding. The code replaces chacters such as *, !, and ) with their corresponding ASCII control encoding. This is often called percent encoding.
private static string PercentEncodeRfc3986(string str)
{
str = HttpUtility.UrlEncode(str, System.Text.Encoding.UTF8);
str = str.Replace("'", "%27").Replace("(", "%28").Replace(")", "%29").Replace("*", "%2A").Replace("!", "%21").Replace("%7e", "~").Replace("+", "%20");
StringBuilder sbuilder = new StringBuilder(str);
for (int i = 0; i < sbuilder.Length; i++)
{
if (sbuilder[i] == '%')
{
if (Char.IsLetter(sbuilder[i + 1]) || Char.IsLetter(sbuilder[i + 2]))
{
sbuilder[i + 1] = Char.ToUpper(sbuilder[i + 1]); sbuilder[i + 2] = Char.ToUpper(sbuilder[i + 2]);
}
}
}
return sbuilder.ToString();
}
After the parameters are sorted in natural byte order and URL encoded, the next step is to concatenate them into a single text string. The following method uses the PercentEncodeRfc3986 method, shown previously, to create the parameter string.
public static String GetSortedParamsAsString(SortedDictionary<String, String> paras, bool isCanonical)
{
String sParams = "";
String sKey = null;
String sValue = null;
String separator = "";
foreach (KeyValuePair<string, String> entry in paras)
{
sKey = entry.Key;
sValue = entry.Value;
if (isCanonical)
{
sKey = PercentEncodeRfc3986(sKey);
sValue = PercentEncodeRfc3986(sValue);
}
sParams += separator + sKey + "=" + sValue;
separator = "&";
}
return sParams;
}
The following ComposeStringToSign method creates the final string to sign. This method simply calls the method GetSortedParamsAsString, shown previously, adds the host URL, the request method that is usually GET or POST, and the resource path, if any. The resulting string is the input value for encryption.
static public string ComposeStringToSign(SortedDictionary<string, string> listPars, string method, string host, string resourcePath)
{
String stringToSign = null;
stringToSign = method + "\n";
stringToSign += host + "\n";
stringToSign += resourcePath + "\n";
stringToSign += GetSortedParamsAsString(listPars, true);
return stringToSign;
}
Following is the string to sign.
GET
ec2.amazonaws.com
/
AWSAccessKeyId=AKIAJDZUQ3CGZ73M2ZIQ&Action=DescribeAvailabilityZones&SignatureMethod=HmacSHA1&SignatureVersion=2&Timestamp=201103-10T16%3A47%3A22Z&Version=2010-11-15
Encrypt the Input String – Create the Signature
Along with the sender’s private key, the string to sign is the input value for the encryption algorithm. Encryption can use either the HMACSha1 or the HMACSha256 algorithm. The .NET System.Security.Cryptography namespace supports both algorithms. In either case the procedure uses the sender’s private key to initialize a signer object that computes a hash of the input string. The final step base64 encodes the hash and returns it as the signature value.
The following method shows HMACSha1 encryption.
public static string GetAWS3_SHA1AuthorizationValue(string AWSAccessKeyId, string AWSSecretAccessKey, string stringToSign)
{
System.Security.Cryptography.HMACSHA1 MySigner =
new System.Security.Cryptography.HMACSHA1(System.Text.Encoding.UTF8.GetBytes(AWSSecretAccessKey));
string SignatureValue =
Convert.ToBase64String(MySigner.ComputeHash(System.Text.Encoding.UTF8.GetBytes(stringToSign)));
return SignatureValue;
}
The following method shows HMACSha256 encryption.
public static string GetAWS3_SHA256AuthorizationValue(string AWSAccessKeyId, string AWSSecretAccessKey, string AmzDate)
{
System.Security.Cryptography.HMACSHA256 MySigner =
new System.Security.Cryptography.HMACSHA256(System.Text.Encoding.UTF8.GetBytes(AWSSecretAccessKey));
string SignatureValue =
Convert.ToBase64String(MySigner.ComputeHash(System.Text.Encoding.UTF8.GetBytes(AmzDate)));
return SignatureValue;
}
All that remains to complete the request URL is to prepend the host URL to the ordered and encoded parameters and append the signature value. The following line of code calls several methods described previously to order and URL encode the request parameters and then adds the Signature parameter name and value.
string queryString = "https://ec2.amazonaws.com/?" + GetSortedParamsAsString(sortedRequestPars, false) +
"&Signature=" + signature;
The result is a request URL with authentication signature.
https://ec2.amazonaws.com/?AWSAccessKeyId=AKIAJDZUQ3CGZ73M2ZIQ&Action=DescribeAvailabilityZones&SignatureMethod=HmacSHA1&SignatureVersion=2&Timestamp=2011-03-10T16:55:46Z&Version=2010-11-15&Signature=4IhhwNE9JBJULbae97vbxRmkV0I=
No comments:
Post a Comment