Her ne kadar Windows Phone ile Azure kullanma taraftarı da olsam; bazen zaruretten başka çözümleri de kullanmak zorunda kalabilirsiniz. Örneğin bir iOS ya da Android uygulamasının Windows Phone sürümünü geliştiriyorsanız ve bu uygulamalar zaten AWS kullanıyorsa, işte o aşamada Azure kullanma şansınız pek kalmıyor.
Amazon; Android ve iOS için bir SDK yayınlamış durumda. Aslına bakarsanız Windows ve .Net için de bir SDK’sı var. Bu SDK’yı indirirseniz bir Windows Phone uygulamasına referans edemeyeceğinizi fark edeceksiniz.
Biraz daha inatçıysanız belki kaynak kodunu indirip, Windows Phone için derlemeye çalışacaksınız ama SDK’nın Hastable, ArrayList, SecureString ve HashAlgoritm gibi Windows Phone tarafında desteklenmeyen bir sürü nesne kullandığını fark edeceksiniz ve pes edeceksiniz.
Hayır! Tabii ki de pes etmeyip, kendi SDK’msınızı yazacaksınız.
Bir kaç parça olarak düşündüğüm bu yazının ilk kısmında Amazon SimpleDB’yi anlatacağım.
Amazon SimpleDB Nedir?
Amazon SimpleDB is a highly available, flexible, and scalable non-relational data store that offloads the work of database administration. Developers simply store and query data items via web services requests, and Amazon SimpleDB does the rest.
Unbound by the strict requirements of a relational database, Amazon SimpleDB is optimized to provide high availability, flexibility, and ease of scalability with little or no administrative burden. Behind the scenes, Amazon SimpleDB creates and manages multiple geographically distributed replicas of your data automatically to enable high availability and data durability. The service charges you only for the resources actually consumed in storing your data and serving your requests. You can change your data model on the fly, and data is automatically indexed for you. With Amazon SimpleDB, you can focus on application development without worrying about infrastructure provisioning, high availability, software maintenance, schema and index management, or performance tuning.
Yukarıda ki tanımı Amazon’un kendi sitesinden arakladım ve hayır Türkçe’ye çevirmeyeceğim ;)
Biraz Daha Hazırlık
SimpleDB, belli bir seviyeye kadar ücretsiz kullanım sağlıyor. Eğer bir AWS hesabınız yoksa ücretsiz olarak bir tane oluşturabilir ve SimpleDB kullanmaya başlayabilirsiniz.
Veritabanına bağlanabilmek için AWS bize iki anahtar veriyor. Bunlar:
- Access Key ID: 20 karakterlik sayı ve rakamlardan bir dizi
- Secret Key ID: 40 karakterlik sayı, rakam ve sembollerden oluşan bir dizi. Bu bilginin gizli kalması gerektiğini, sizden ve Amazon’dan başka hiç kimsenin bilmemesi gerektiğini unutmayın!
Hazırlığın Son Kısmı
Kodlama süresince kolaylık sağlaması için yukarıdaki bilgileri saklayacağım bir sınıf tanımlayacağım; ama siz bu bilgileri daha güvenli bir şekilde saklamalısınız.
namespace Haplois.Cloud.AWS.Common
{
public class Configuration
{
public const string Key = "BFSCUDCFRFDGCFDEUFDV";
public const string SecretKey = "***secret key***";
public const string ServiceVersion = "2009-04-15";
public const string SimpleDbServiceUrl = "https://sdb.amazonaws.com";
public const string SignatureVersion = "2";
public const string SignatureMethod = "HmacSHA256";
}
}
Yukarıdaki yukarıda size henüz bahsetmediğim bir kaç tanımlama var bunlar;
- ServiceVersion: Amazon AWS servislerini versiyonlayarak geriye uyum sağlıyor. Bizim bu örneklerde kullanacağımız versiyonu sakladığımız sabitimiz.
- SignatureVersion: Servise göndereceğimiz istekleri imzalayacağımız algoritmanın versiyonunu burada saklıyoruz.
- SignatureMethod: Kullanacağımız imzalama metodu.
İstekleri İmzalama
Amazon Web Servislerini sorgulayabilmek için kimliğimizi doğrulamamız gerekli. Bunu da istekleri imzlayarak sağlıyoruz. SimpleDB’ye kimlik doğrulamak için imzamızı üç basit aşamada oluşturabiliyoruz. Bu aşamalar:
- Servise göndereceğimiz REST isteğini oluşturuyoruz.
- Bu isteğe göre imzalamamız gereken metni oluşturuyor ve bu metni HMAC’a göre imzalıyoruz.
- İmzayı ve Access Key ID’yi REST isteğine ekleyip; isteği AWS’ye gönderiyoruz.
REST İsteğini Oluşturma
Bu en kolay kısmı. REST isteği bu aşamada key’ler ve value’lar dan oluşan bir Dictionary aslında. Bu istek çağıracağımız fonksiyona göre bazı değişiklikler içerse de aşağıdaki değerler sürekli bulunmak zorunda.
- AWSAccessKeyId
- SignatureVersion
- SignatureMethod
- Version
- Timestamp
Bunlardan ilk dördünü Configuration sınıfımızda tanımladık zaten. Timestamp ise ISO8601 (“yyyy-MM-dd\\THH:mm:ss.fff\\Z”) formatında tarihi ifade ediyor. Bu tarihi aşağıdaki kodla kolayca oluşturabiliriz:
public const string ISO8601DateFormat = "yyyy-MM-dd\\THH:mm:ss.fff\\Z";
public static string FormattedCurrentTimestampISO8601
{
get
{
return GetFormattedTimestampISO8601(0);
}
}
public static string GetFormattedTimestampISO8601(int minutesFromNow)
{
DateTime dateTime = DateTime.UtcNow.AddMinutes(minutesFromNow);
DateTime formatted = new DateTime(
dateTime.Year,
dateTime.Month,
dateTime.Day,
dateTime.Hour,
dateTime.Minute,
dateTime.Second,
dateTime.Millisecond,
DateTimeKind.Local
);
return formatted.ToString(
ISO8601DateFormat,
CultureInfo.InvariantCulture
);
}
Yukarıdaki kodu, AWS’nin .Net SDK’sından kopyaladım.
SignatureVersion için güncel değer ben bu yazıyı yazdığım sırada 2’ydi. 1 numaralı versiyon eski ve yeni sürümlerde destelenmeyecek.
SignatureMethod kullandığınız algoritmaya göre “HmacSHA1” ya da “HmacSHA256” olabilir. Ben SHA256’ya daha fazla güvendiğimden “HmacSHA256” kullanıyorum; ama tamamen duygusal.
İmzalanacak Metni Oluşturma
- REST isteğinde AWS’ye göndereceğimiz parametreleri alfabetik olarak sıralayın.
- Parametre değerlerini ve isimlerini şu kurallara göre düzenleyin:
- RFC 3986’da rezerve edilmemiş herhangi bir karakter encode edilmemeli. (A-Z ve a-z İngilizce harfler, 0-9 arası rakamlar, tire “-”, altçizgi “_”, nokta ve tilde “~” )
- Encode edilecek karakterlerin ASCII’si hex olarak % işaretinden sonra 0-F arası rakamlar kullanılarak encode edilmeli. (%9a gerçersiz, %9A geçerli)
- UTF-8 karakterler byte byte encode edilmeli (%XY%ZA…)
- Boşluk karakteri %20 olarak encode edilmeli (HttpUtility.UrlEncode’un yaptığı gibi “+” olarak değil)
- Parametreleri ve değerlerini “=” ile ayırın, değeri boş ise bile “=” kullanın.
- Parametreleri birbirinden “&” ile ayrın.
Not: Alfabetik olarak ilk sırada olmasa bile ASWAccessKeyId en başta olmalı.
Öncelikle kendi UrlEncode fonksiyonumuzu yazmalıyız. Bu fonksiyonu doğrudan Amazon SDK’sından aldım.
public const string ValidUrlCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
public static string UrlEncode(string data, bool path)
{
StringBuilder encoded = new StringBuilder(data.Length * 2);
string unreservedChars = String.Concat(
ValidUrlCharacters,
(path ? "/:" : "")
);
foreach (char symbol in System.Text.Encoding.UTF8.GetBytes(data))
{
if (unreservedChars.IndexOf(symbol) != -1)
{
encoded.Append(symbol);
}
else
{
encoded.Append("%").Append(String.Format("{0:X2}", (int)symbol));
}
}
return encoded.ToString();
}
Bu kadar açıklamadan sonra imzalayacağımız metni aşağıdaki kod ile oluşturabiliriz.
public static string CalculateStringToSignSimbleDb(IDictionary<string, string> parameters, string serviceUrl)
{
StringBuilder data = new StringBuilder("POST\n"); ;
Uri endpoint = new Uri(serviceUrl);
data.Append(endpoint.Host.ToLowerInvariant());
data.Append("\n");
string uri = endpoint.AbsolutePath;
if (uri == null || uri.Length == 0)
{
uri = "/";
}
data.Append(uri);
data.Append("\n");
if (parameters.ContainsKey("AWSAccessKeyId"))
{
data.Append(Helpers.UrlEncode("AWSAccessKeyId", false));
data.Append("=");
data.Append(Helpers.UrlEncode(parameters["AWSAccessKeyId"], false));
data.Append("&");
}
foreach (KeyValuePair pair in parameters.OrderBy(p => p.Key.ToUpperInvariant()))
{
if (pair.Key == "AWSAccessKeyId") continue;
if (pair.Value != null)
{
data.Append(Helpers.UrlEncode(pair.Key, false));
data.Append("=");
data.Append(Helpers.UrlEncode(pair.Value, false));
data.Append("&");
}
}
string result = data.ToString();
return result.Remove(result.Length - 1);
}
Burada “parameters“ parametresi REST isteğimizde göndereceğimiz parametreleri, “serviceUrl” ise bağlanacağımız servisin tam adresini içeriyor. Şu aşamada bu adres, Configuration.SimpleDbServiceUrl’e eşit.
Bu metni oluşturmadan önce REST ve sonra yapmamız gereken işlemler için aşağıdaki kodu yazıyoruz
private static void ConfigureParameters(IDictionary<string, string> values)
{
if (values.ContainsKey("Signature"))
{
values.Remove("Signature");
}
values["AWSAccessKeyId"] = Configuration.Key;
values["SignatureVersion"] = Configuration.SignatureVersion;
values["SignatureMethod"] = Configuration.SignatureMethod;
values["Timestamp"] = Helpers.FormattedCurrentTimestampISO8601;
values["Version"] = Configuration.ServiceVersion;
var sign = CalculateStringToSignSimbleDb(values, Configuration.SimpleDbServiceUrl);
var auth = Helpers.HMACSign(sign, Configuration.SecretKey, HashAlgorithmType.SHA256);
values["Signature"] = auth;
}
Bu kodu kısaca açıklayayım isterseniz. Öncelikle, parametrelerimiz arasında istek imzalanmadan önce “Signature” bulunamaz, bu nedenle eğer varsa siliyoruz. Eklenmesi zorunlu olan değerleri ekliyor ya da zaten varsalar güncelliyoruz. Bu aşamada, imzalayacağımız metni de oluşturuyor ve HMAC ile imzalıyoruz.
İmzalama işleminden sonra hesapladığımız değeri “Signature” parametresi olarak isteğe ekliyoruz.
İçeriği HMAC ile imzalamak için aşağıdaki kodu kullanıyoruz:
public static string HMACSign(string dataToSign, string key, HashAlgorithmType algoritm)
{
var hashKey = Encoding.UTF8.GetBytes(key);
HMAC hasher = null;
switch (algoritm)
{
case HashAlgorithmType.SHA1:
hasher = new HMACSHA1(hashKey);
break;
case HashAlgorithmType.SHA256:
hasher = new HMACSHA256(hashKey);
break;
}
var hashedData = hasher.ComputeHash(Encoding.UTF8.GetBytes(dataToSign));
return Convert.ToBase64String(hashedData);
}
Bu fonksiyon aşağıdaki enum'a ihtiyaç duyuyor:
public enum HashAlgorithmType
{
SHA1,
SHA256
}
Bu makaledeki kodları buradan indirebilirsiniz.
Devamı yarın…