diff --git a/Amazon.TVMClient/AwsTemporaryCredentialFactory.cs b/Amazon.TVMClient/AwsTemporaryCredentialFactory.cs
new file mode 100644
index 000000000000..10dcb2b4fc1a
--- /dev/null
+++ b/Amazon.TVMClient/AwsTemporaryCredentialFactory.cs
@@ -0,0 +1,70 @@
+/*
+ * Author: David Iffland, Heavy Code LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ */
+
+using System;
+
+namespace AWSSDK.Amazon.TVMClient
+{
+ public class AwsTemporaryCredentialFactory
+ {
+ private const string AccessKeyName = "accessKey";
+ private const string SecretKeyName = "secretKey";
+ private const string SecurityTokenKeyName = "securityToken";
+ private const string ExpirationDateKeyName = "expirationDate";
+
+
+
+ ///
+ /// Used to create a set of temporary security credentials from the response provided by the
+ /// Token Vending Machine.
+ ///
+ /// The response from the Token Vending Machine
+ /// A set of temporary AWS credentials
+ public static AwsTemporaryCredentials Create(string credentialString)
+ {
+ AwsTemporaryCredentials credentials = new AwsTemporaryCredentials
+ {
+ AccessKey = ExtractElement(credentialString, AccessKeyName),
+ SecretKey = ExtractElement(credentialString, SecretKeyName),
+ SecurityToken =
+ ExtractElement(credentialString, SecurityTokenKeyName),
+ ExpirationDate =
+ AwsTemporaryCredentials.GetExpirationTimeFromMilliseconds(
+ ExtractElement(credentialString,
+ ExpirationDateKeyName))
+ };
+
+ return credentials;
+
+ }
+
+ ///
+ /// Used to extract a piece of data from a json string.
+ ///
+ /// This is a C# port of the Java version written by Amazon.com
+ /// The raw string to exctract the element from.
+ /// the name of the piece of data to extract.
+ /// The value of the exctracted element.
+ private static String ExtractElement(String json, String element)
+ {
+ bool hasElement = (json.IndexOf(element) != -1);
+ if (hasElement)
+ {
+ int elementIndex = json.IndexOf(element);
+ int startIndex = json.IndexOf("\"", elementIndex);
+ int endIndex = json.IndexOf("\"", startIndex + 1);
+
+ return json.Substring(startIndex + 1, endIndex - (startIndex + 1));
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/Amazon.TVMClient/AwsTemporaryCredentials.cs b/Amazon.TVMClient/AwsTemporaryCredentials.cs
new file mode 100644
index 000000000000..04cbc3fe454d
--- /dev/null
+++ b/Amazon.TVMClient/AwsTemporaryCredentials.cs
@@ -0,0 +1,52 @@
+/*
+ * Author: David Iffland, Heavy Code LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ */
+
+using System;
+
+namespace AWSSDK.Amazon.TVMClient
+{
+ ///
+ /// Used to contain a set of temporary credentials for Amazon AWS.
+ ///
+ public class AwsTemporaryCredentials
+ {
+ public string AccessKey { get; set; }
+ public string SecretKey { get; set; }
+ public string SecurityToken { get; set; }
+ public DateTime ExpirationDate { get; set; }
+
+ ///
+ /// Handles converting milliseconds since 01-01-1970 into a useable DateTime.
+ ///
+ /// The number of milliseconds since 01-01-1970 as a string
+ /// A DateTime that is 01-01-1970 plus the number of milliseconds
+ public static DateTime GetExpirationTimeFromMilliseconds(string milliseconds)
+ {
+ long longMillseconds;
+ Int64.TryParse(milliseconds, out longMillseconds);
+
+ return GetExpirationTimeFromMilliseconds(longMillseconds);
+
+ }
+
+ ///
+ /// Handles converting milliseconds since 01-01-1970 into a useable DateTime.
+ ///
+ /// The number of milliseconds since 01-01-1970
+ /// A DateTime that is 01-01-1970 plus the number of milliseconds
+ public static DateTime GetExpirationTimeFromMilliseconds(long milliseconds)
+ {
+ var ticks = milliseconds*TimeSpan.TicksPerMillisecond;
+ var correctedDate = new DateTime(1970, 1, 1).Add(new TimeSpan(ticks));
+ return correctedDate;
+ }
+
+ }
+}
diff --git a/Amazon.TVMClient/TVMClient.cs b/Amazon.TVMClient/TVMClient.cs
new file mode 100644
index 000000000000..ad8d3b9cfc8c
--- /dev/null
+++ b/Amazon.TVMClient/TVMClient.cs
@@ -0,0 +1,241 @@
+/*
+ * Author: David Iffland, Heavy Code LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ */
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Security.Cryptography;
+using System.Text;
+using Amazon.Util;
+
+namespace AWSSDK.Amazon.TVMClient
+{
+ public class TVMClient
+ {
+ public string TvmUrl { get; set; }
+
+
+ ///
+ /// Called to initially register a device with the TVM.
+ ///
+ /// A 32 character device id.
+ /// A 32 character user defined key
+ /// The callback method
+ public void RegisterDevice(string deviceId, string tvmKey, Action callback)
+ {
+ IDictionary parameters = new Dictionary();
+
+ parameters["uid"] = deviceId;
+ parameters["key"] = tvmKey;
+
+ string queryString = AWSSDKUtils.GetParametersAsString(parameters);
+
+ var uri = new Uri(string.Format("{0}/registerdevice?{1}", TvmUrl, queryString));
+
+ var hwr = WebRequest.Create(uri) as HttpWebRequest;
+ if (hwr == null)
+ {
+ throw new NullReferenceException("Could not create HttpWebRequest");
+ }
+
+ hwr.Method = "GET";
+
+ var tokenPackage = new TemporaryTokenPackage() {DeviceId = deviceId, Request = hwr};
+
+ AsyncCallback responseHandler = (async) =>
+ {
+ TemporaryTokenPackage temporaryTokenPackage =
+ (TemporaryTokenPackage) async.AsyncState;
+ HttpWebRequest request = temporaryTokenPackage.Request;
+
+ HttpWebResponse response = null;
+ try
+ {
+ response = (HttpWebResponse) request.EndGetResponse(async);
+ callback(true, null);
+
+ }
+ catch (WebException we)
+ {
+ if (((HttpWebResponse) we.Response).StatusDescription ==
+ "Conflict")
+ {
+ callback(true, null);
+ }
+ else
+ {
+ callback(false, we);
+ }
+
+ }
+ };
+
+ hwr.BeginGetResponse(responseHandler, tokenPackage);
+ }
+
+ ///
+ /// Called to retrieve a set of temporary credentials from the TVM.
+ ///
+ /// The same 32 character device ID used in RegisterDevice
+ /// The same 32 character TVM key used in RegisterDevice
+ /// The callback method
+ public void GetTemporaryCredentials(string deviceId, string tvmKey, Action callback)
+ {
+
+ string timeStamp = AWSSDKUtils.GetFormattedTimestampISO8601(0);
+ string signature = AWSSDKUtils.HMACSign(timeStamp, tvmKey, new HMACSHA256());
+
+ IDictionary parameters = new Dictionary();
+ parameters["uid"] = deviceId;
+ parameters["timestamp"] = timeStamp;
+ parameters["signature"] = signature;
+
+
+ string queryString = AWSSDKUtils.GetParametersAsString(parameters);
+ var uri = new Uri(string.Format("{0}/gettoken?{1}", TvmUrl, queryString));
+
+
+ var hwr = WebRequest.Create(uri) as HttpWebRequest;
+ if (hwr == null)
+ {
+ throw new NullReferenceException("Could not create HttpWebRequest");
+ }
+
+ AsyncCallback responseHandler = async =>
+ {
+ var request = (HttpWebRequest)async.AsyncState;
+
+ HttpWebResponse response = null;
+ try
+ {
+ response = (HttpWebResponse)request.EndGetResponse(async);
+
+ }
+ catch (WebException we)
+ {
+ callback(null, we);
+ }
+
+ if (IsGoodResponse(response))
+ {
+ Stream stream = response.GetResponseStream();
+ if (stream != null)
+ {
+ var sr = new StreamReader(stream);
+
+ var responseText = sr.ReadToEnd();
+
+ sr.Close();
+
+ var dataToDecrypt = Convert.FromBase64String(responseText);
+
+ var plainText = Decrypt(dataToDecrypt, tvmKey);
+
+ var temporaryCredentials =
+ AwsTemporaryCredentialFactory.Create(plainText);
+
+ callback(temporaryCredentials, null);
+ }
+ else
+ {
+ callback(null, new WebException("Response stream was null"));
+ }
+
+ return;
+
+ }
+ else
+ {
+ string responseText = "noresponse";
+
+ if (response != null)
+ {
+ responseText = response.StatusCode.ToString();
+ }
+
+ callback(null, new WebException("Bad web response, StatusCode=" + responseText));
+
+ }
+ };
+
+ hwr.BeginGetResponse(responseHandler, hwr);
+ }
+
+
+
+ ///
+ /// Override this to take a closer look at the response, for example to look at the status code.
+ ///
+ /// Default implementation looks for HttpResponse.StatusCode == 200.
+ ///
+ ///
+ ///
+ protected virtual bool IsGoodResponse(HttpWebResponse response)
+ {
+ if (response == null)
+ return false;
+
+ return response.StatusCode == HttpStatusCode.OK;
+ }
+
+ public string Decrypt(byte[] dataToDecrypt, string secretKey)
+ {
+ var secretKeyToUse = DecodeHex(secretKey.ToCharArray());
+ var cipher = CipherUtilities.GetCipher("AES/CBC/PKCS7Padding");
+
+ cipher.Init(false, new KeyParameter(secretKeyToUse));
+
+
+ int size = cipher.GetOutputSize(dataToDecrypt.Length);
+ var results = new byte[size];
+
+ int olen = cipher.ProcessBytes(dataToDecrypt, 0, dataToDecrypt.Length, results, 0);
+ cipher.DoFinal(results, olen);
+
+ var result = Encoding.UTF8.GetString(results, 0, results.Length);
+
+ return result;
+ }
+
+ ///
+ /// Used to decode a plain text key into a key that can be used to decrypt. The data being passed in is assumed to be a
+ /// series of hex digits and this converts those 2-digit hex bytes into a single byte array.
+ ///
+ /// This is adapated from the org.apache.commons.codec.binary.Hex java source code at http://kickjava.com/src/org/apache/commons/codec/binary/Hex.java.htm
+ ///
+ /// An array of characters containing hex digits
+ /// A byte array containing the decoded hex data in binary format.
+ public static byte[] DecodeHex(char[] data)
+ {
+
+ int len = data.Length;
+
+ if ((len & 0x01) != 0)
+ {
+ throw new DataLengthException("Odd number of characters.");
+ }
+
+ var outresult = new byte[len >> 1];
+
+ // two characters form the hex value.
+ for (int i = 0, j = 0; j < len; i++)
+ {
+ var f = Convert.ToInt32(data[j++].ToString(), 16) << 4;
+ f = f | Convert.ToInt32(data[j++].ToString(), 16);
+
+ outresult[i] = (byte)(f & 0xFF);
+ }
+
+ return outresult;
+ }
+
+ }
+}
diff --git a/Amazon.TVMClient/TemporaryTokenPackage.cs b/Amazon.TVMClient/TemporaryTokenPackage.cs
new file mode 100644
index 000000000000..b0dd09526781
--- /dev/null
+++ b/Amazon.TVMClient/TemporaryTokenPackage.cs
@@ -0,0 +1,23 @@
+/*
+ * Author: David Iffland, Heavy Code LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ */
+
+using System.Net;
+
+namespace AWSSDK.Amazon.TVMClient
+{
+ ///
+ /// Package used to combine necessary information for async callback data manipulation.
+ ///
+ public class TemporaryTokenPackage
+ {
+ public HttpWebRequest Request { get; set; }
+ public string DeviceId { get; set; }
+ }
+}
\ No newline at end of file