Saturday, July 17, 2010 

WordPress, it's what the cool kids are doing

About time my blog "grew up" a bit, and moved to its own domain: blog.tinisles.com. I've splashed out and set-up a WordPress blog at DreamHost.

Expect an article on the new blog detailing my geek adventures setting up WordPress and tranferring the articles over!

Sunday, January 10, 2010 

Should you trust lastpass.com?

lastpass.com looks like a handy online service for storing all your website passwords. Browser extensions are available to make the whole process easier. If you use the web from several PCs it does sound nice to have all your passwords available and synced between your PCs.

First thought was wether I should hand over my passwords to the 3rd party, hopefully they are storing them with some serious crypto. Perusing their FAQ I found some info on this:

We only support keeping the encryption done on your computer so LastPass can't see your sensitive data
...your sensitive data is always encrypted and decrypted locally on your computer before being synchronized. Your master password never leaves your computer and your key never leaves your computer. No one at LastPass (or anywhere else) can decrypt your data without you giving up your password (we will never ask you for it). Your key is created by taking a SHA-256 hash of your password. When you login, we make a hash of your username concatenated with your password, and that hash is what's sent to verify if you can download your encrypted data.

So they are encrypting everything with a key derived from your password - plus they don't know your password - so they CANNOT access any of your passwords. Nice. But why stop there? Let's fire up fiddler to make sure they definitely don't have my passwords.

I've created a junk account on lastpass username: russell.sayers.junk@gmail.com, password: test1234 (the account will be gone by the time you read this!). The first form I see submitted to the server is when I create my account:

username russell.sayers.junk@gmail.com
hash 53c81a859a3f3d4dc3762d3a47bab07fad7ad3f2673724deb20fb420e8bdc03a
password ********
password2 ********
password_hinttesting
timezone2 +10:00,1
language2 en-US
agree on
agreeupload on
loglogins on
improve on
json 1

I don't see my password going to their servers in the clear. I can confirm that the hash getting sent is geniune by creating the same hash elsewhere via an online SHA256 generator, or writing some code myself. Try it yourself, the hash created is SHA256(SHA256(username + password) + password) - everything checks out. All the C# code to verify the encryption/hashing is at the end of the article.

The login form:

methodweb
hash53c81a859a3f3d4dc3762d3a47bab07fad7ad3f2673724deb20fb420e8bdc03a
usernamerussell.sayers.junk@gmail.com
encrypted_usernameT2tBleI3PxuLOoNEwNkv5PZ/rYr5dDIoYZS+We4vER4=
otp
gridresponse
trustlabel
uuid
sesameotp
emailrussell.sayers.junk@gmail.com
password

Again the same hash is being used to verify me when I login. I can see an encrypted username is being sent (although I'm not sure why?), rooting around in the javascript I can see the key being used for encyption is SHA(username+password). Importantly this is different to the hash being used to authenticate me - as we don't want the server to be able to decypt anything encypted by the client, i.e. they would have to know my password to derive the same key on their side.

The form to add a new website:

hasplugin0
extjs1
search
purgeext0
deleteext
undeleteext0
ajax1
ob
basic_auth0
isbookmark0
openid_url
aid0
useurid0
fromwebsite1
namepyJlY+AX0Aoczlx50hwlHg==
grouping
origurl
url66616365626f6f6b2e636f6d
usernameICkGIGAn7SIk16iKNkl3DA==
passworddl78FYUSIdsxxSdkBuBWEA==
extrafaESoIpzmCQg5PeHpXN0GQ==
We can see here the client is sending encyrypted versions of name, username, password, and the extra info. Again this is encrypted with a key we only have on our client - no one at lastpass could have this key. For some reason the URL is being sent in a HEX representation of the string - not sure why they aren't just sending the string?

So when I log back into lastpass, I can drill down into this entry and see it again. Let's make sure everything looks okay here. The HTML that renders this screen is available in fiddler:

<tr>
  <td class='col1'>Name</td>
  <td><input name='name' id='name' type='text' value='pyJlY+AX0Aoczlx50hwlHg==' style='width: 250px'></td>
</tr>
...
<tr>
  <td class='col1'>Username</td>
  <td><input name='username' id='idusername' type='text' value='SgVkuVKH4MjkP+Saz64UhA=='> </td>
</tr>
...
<tr>
  <td class='col1'>Notes</td>
  <td><textarea name='extra' id='extra' rows='6' cols='35'>1rZ3sSuggtdavyCu446GZA==</textarea></td>
</tr> 
The server is sending down our encrypted details, and relying on the client to decrypt everything.

So, yes - you CAN trust lastpass.com with your passwords! Don't just take my word for it; fire up Fiddler, and compare the hashed/encrypted values with what you expect.

Lastly the c# code to confirm the AES encrypted strings above are geniune.

static void Main(string[] args)
{
    string username = "russell.sayers.junk@gmail.com";
    string password = "test1234";
    string hash_auth = ByteArrayToHexString(SHA256(ByteArrayToHexString(SHA256(username+password)) + password));
    byte[] hash_key = SHA256(username + password);

    Console.WriteLine("hash for authentication => " + hash_auth);
    Console.WriteLine("hash for encryption => " +  ByteArrayToHexString(hash_key));
    Console.WriteLine("'{0}' encrypted => {1}", username, Encrypt(username, hash_key, ""));
    Console.WriteLine("'{0}' encrypted => {1}", "facebook", Encrypt("facebook", hash_key, ""));
    Console.WriteLine("'{0}' encrypted => {1}", "cryptolearner", Encrypt("cryptolearner", hash_key, ""));
    Console.WriteLine("'{0}' encrypted => {1}", "password", Encrypt("password", hash_key, ""));
    Console.WriteLine("'{0}' encrypted => {1}", "no notes", Encrypt("no notes", hash_key, ""));
}

static byte[] SHA256(string data)
{
    byte[] indata = Encoding.UTF8.GetBytes(data);
    SHA256 shaM = new SHA256Managed();
    return shaM.ComputeHash(indata);
}

/// <remarks>From http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/3928b8cb-3703-4672-8ccd-33718148d1e3</remarks>
static string ByteArrayToHexString(byte[] data)
{
    StringBuilder sb = new StringBuilder(data.Length * 2);
    foreach (byte b in data)
    {
        sb.AppendFormat("{0:x2}", b);
    }
    return sb.ToString();
}

/// <remarks>From http://stackoverflow.com/questions/1079131/c-aes-256-encryption</remarks>
static public string Encrypt(string plaintext, byte[] KeyBytes, string InitialVector)
{
    byte[] PlainTextBytes = Encoding.UTF8.GetBytes(plaintext);
    byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
    RijndaelManaged SymmetricKey = new RijndaelManaged();
    SymmetricKey.Mode = CipherMode.ECB;
    SymmetricKey.Padding = PaddingMode.PKCS7;
    ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes);
    MemoryStream MemStream = new MemoryStream();
    CryptoStream CryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write);
    CryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
    CryptoStream.FlushFinalBlock();
    byte[] CipherTextBytes = MemStream.ToArray();
    MemStream.Close();
    CryptoStream.Close();
    return Convert.ToBase64String(CipherTextBytes);
}

Labels:

Tuesday, January 05, 2010 

Expanding on Josh Smith's WPF MVVM app

Over the holidays I've got a start on a new project idea. I decided to do the simple UI of the project as an MVVM WPF app. First thing to do was to google up some sample MVVM WPF app. There are some very good example apps out there, some honourable mentions: Sonic, Karl Shifflett's Cipher, and Josh Smith's MVVM Demo App (some good comments on the accompanying blog post: My MVVM article in MSDN Magazine)

Josh's demo app is simple enough for me to quickly get my head around some WPF and MVVM concepts. But the article includes a challenge :) :

The application does not have support for deleting or editing an existing customer, but that functionality, and many other features similar to it, are easy to implement by building on top of the existing application architecture.

The following is a quick summary on how I added editing to the demo app. This is TOTALLY up for debate. I'm interested to know if I could've taken an easier approach. In summary, I've added a RelayCommand '_editCommand' up in MainWindowViewModel to display the edit tab. I pass this command down to CustomerViewModel so I can bind it to a new button on AllCustomersView. Am I creating ViewModel's that are too tightly coupled?

  • MainWindowViewModel.cs
    • added a private field:
      ICommand _editCommand;
    • in the constructor this is pointed to a method in MainWindowViewModel:
      _editCommand = new RelayCommand(cust => EditCustomer(cust as CustomerViewModel));
    • constructors for CustomerViewModel and AllCustomersViewModel are now passed _editCommand
    • added method EditCustomer to display the workspace:
      void EditCustomer(CustomerViewModel workspace)
      {
          WorkspaceViewModel exisitingModel = Workspaces.FirstOrDefault(cust => cust is CustomerViewModel && (cust as CustomerViewModel) ==  workspace);
          if (exisitingModel == null)
          {
              this.Workspaces.Add(workspace);
          }
          this.SetActiveWorkspace(workspace);
      }
      
  • AllCustomersViewModel.cs
    • added a private field:
      ICommand _editCommand;
    • _editCommand is set by a new parameter in the constructor (passed in by MainWindowViewModel)
    • _editCommand is passed to the CustomerViewModel constructor in OnCustomerAddedToRepository and CreateAllCustomers.
  • CustomerViewModel.cs
    • added a private field:
      ICommand _editCommand;
    • _editCommand is set by a new parameter in the constructor
    • fixed the _CustomerType defaulting to 'not specified':
      _customerType = (this.IsNewCustomer) ? Strings.CustomerViewModel_CustomerTypeOption_NotSpecified :
          (_customer.IsCompany) ? Strings.CustomerViewModel_CustomerTypeOption_Company : 
          Strings.CustomerViewModel_CustomerTypeOption_Person;
    • exposed _EditCustomer as a public property: EditCustomer
  • AllCustomersView.xaml - bound a new 'Edit' button to the EditCommand
    <GridViewColumn Header="Action">
        <GridViewColumn.CellTemplate>
            <DataTemplate>
                <StackPanel>
                    <Button Content="Edit" 
                            Command="{Binding EditCustomer}" 
                            CommandParameter="{Binding}"/>
                </StackPanel>
            </DataTemplate>
        </GridViewColumn.CellTemplate>
    </GridViewColumn>

Labels: ,

Tuesday, December 01, 2009 

Wifi Geolocation

Playing around with Google Latitude I was VERY surprised to see Latitude work out my exact location. How are they doing this? I don't have a GPS in my netbook?

Turns out they use wifi location to do this. When Latitude determines your location it gathers up a list of all the nearby Wifi SSIDs / MAC addresses and sends this to Google. Google have a database of Wifi access point locations created by driving cars around. This database, in combination with the nearby Wifi details, is used to determine your location. Pretty amazing that this actually works! Skyhook wireless is the first company I heard about building a Wifi database.

Always curious to see this stuff in action I fired up Fiddler to see exactly what is being sent to determine my location.

Request:

POST /loc/json HTTP/1.1

{
  "access_token": "2:Fv--0aj9-Vr4_3ir:8uFyciMoILwtrXry", 
  "host": "lfkq9vbe9u4sg98ip8rfvf00l7atcn3d.ig.ig.gmodules.com", 
  "radio_type": "unknown", 
  "request_address": false, 
  "version": "1.1.0", 
  "wifi_towers": [
    {
      "mac_address": "00-0b-6b-81-7b-14", 
      "signal_strength": -72, 
      "ssid": "OA0509-0077-1-M"
    }, 
    {
      "mac_address": "00-0b-6b-81-7d-0d", 
      "signal_strength": -78, 
      "ssid": "OA0509-0096-1-1"
    }, 
    {
      "mac_address": "00-0b-6b-81-7d-0f", 
      "signal_strength": -85, 
      "ssid": "OA0509-0052-2-1"
    }, 
    {
      "mac_address": "00-0b-6b-81-7d-29", 
      "signal_strength": -80, 
      "ssid": "OA0509-0096-2-1"
    }, 
    {
      "mac_address": "00-0b-6b-81-7d-8a", 
      "signal_strength": -82, 
      "ssid": "OA0509-0101-1-1"
    }, 
    {
      "mac_address": "00-0b-6b-81-7e-bd", 
      "signal_strength": -79, 
      "ssid": "OA0509-0052-1-1"
    }, 
    {
      "mac_address": "00-22-3f-9b-f1-a4", 
      "signal_strength": -61, 
      "ssid": "2/107"
    }
  ]
}

Response:

{
  "location": {
    "accuracy": 165.0, 
    "latitude": -33.874097499999998, 
    "longitude": 151.20640280000001
  }
}

Want to add geolocation to your own web application? Check out the Google Gears Geolocation API, or the Firefox's geolocation object

Sunday, September 20, 2009 

Sending a DKIM Signed Email from C#

First off this code is intended to be instructional only! Yes it is possible to send a DKIM email from .net's SmtpClient. But there are a few hacks/warnings to go with the following code. I'm pretty sure DKIM is designed for signatures to be added by the STMP server. All the emails I've seen with DKIM signatures sign headers that would only be available to the server, e.g: Message-ID and Received. What does the RFC 4871 have to say about this:

Note that verifiers may treat unsigned header fields with extreme skepticism, including refusing to display them to the end user or even ignoring the signature if it does not cover certain header fields. For this reason, signing fields present in the message such as Date, Subject, Reply-To, Sender, and all MIME header fields are highly advised.
Second: We need to sign the message body exactly as SmtpClient is going to deliver it to the SMTP server. Unfortunately MailMessage doesn't give us access to this. I've found some code to do the Quoted-printable encoding here: Nice Clean C# Generate Quoted-Printable. Still we couldn't guarantee we are going to hash the body exactly as SmtpClient is going to send. In this example I'm just replacing the carriage returns for an extremely simple sample email.

Let's get started. First use OpenSSL to generate your public and private keys: DomainKeys Public/Private Key-pair Generation. Now add the public key to the relevant TXT record on the domain you intend to send email from. I bought a cheap .info domain for this demo, and pointed the registrar to ZoneEdit. I had no trouble adding to TXT record to my domain via ZoneEdit.

...and now the code: (again I'm using Bouncy Castle Crypto APIs to create the signature)

string smtp = "~~ YOUR STMP SERVER HERE ~~";
string from = "russ@dkimtester.info";
string subject = "dkim test email";
string to = "check-auth@verifier.port25.com";
string body = "This is the body of the message." + Environment.NewLine + "This is the second line";
string base64privatekey = @"-----BEGIN RSA PRIVATE KEY-----
~~ YOUR PRIVATE KEY HERE ~~
-----END RSA PRIVATE KEY-----";

HashAlgorithm hash = new SHA256Managed();
// HACK!! simulate the quoted-printable encoding SmtpClient will use
string hashBody = body.Replace(Environment.NewLine, "=0D=0A") + Environment.NewLine;
byte[] bodyBytes = Encoding.ASCII.GetBytes(hashBody);
string hashout = Convert.ToBase64String(hash.ComputeHash(bodyBytes));
// timestamp  - seconds since 00:00:00 on January 1, 1970 UTC
TimeSpan t = DateTime.Now.ToUniversalTime() - DateTime.SpecifyKind(DateTime.Parse("00:00:00 January 1, 1970"), DateTimeKind.Utc);

string signatureHeader = "v=1; " +
 "a=rsa-sha256; " +
 "c=relaxed/relaxed; " +
 "d=dkimtester.info; " +
 "s=p; " +
 "t=" + Convert.ToInt64(t.TotalSeconds) + "; " +
 "bh=" + hashout + "; " +     
 "h=From:To:Subject:Content-Type:Content-Transfer-Encoding; " +
 "b="; 

string canonicalizedHeaders =
"from:" + from + Environment.NewLine +
"to:" + to + Environment.NewLine +
"subject:" + subject + Environment.NewLine +
@"content-type:text/plain; charset=us-ascii
content-transfer-encoding:quoted-printable
dkim-signature:" + signatureHeader;

TextReader reader = new StringReader(base64privatekey);
Org.BouncyCastle.OpenSsl.PemReader r = new Org.BouncyCastle.OpenSsl.PemReader(reader);
AsymmetricCipherKeyPair o = r.ReadObject() as AsymmetricCipherKeyPair;
byte[] plaintext = Encoding.ASCII.GetBytes(canonicalizedHeaders);
ISigner sig = SignerUtilities.GetSigner("SHA256WithRSAEncryption");
sig.Init(true, o.Private);
sig.BlockUpdate(plaintext, 0, plaintext.Length);
byte[] signature = sig.GenerateSignature();
signatureHeader += Convert.ToBase64String(signature);

MailMessage message = new MailMessage();
message.From = new MailAddress(from);
message.To.Add(new MailAddress(to));
message.Subject = subject;
message.Body = body;

message.Headers.Add("DKIM-Signature", signatureHeader);
SmtpClient client = new SmtpClient(smtp);
client.Send(message);
Console.Write("sent to: " + to);

An email sent to gmail gets the cool "signed-by" info:

Labels: ,

Sunday, August 30, 2009 

Verifying a DKIM signature in C#

Quick background: DomainKeys Identified Mail (DKIM) is a digital signature and hash which are placed on your emails to verify they are coming from the domain they claim to be. One step in the defence against SPAM and phishing emails.

What does the signature look like. Here's a signature grabbed from an email sent from Yahoo mail:

DKIM-Signature: 
 v=1; 
 a=rsa-sha256; 
 c=relaxed/relaxed; 
 d=yahoo.com.au; 
 s=s1024; 
 t=1250214285; 
 bh=cHSvmWBBhlnc7HYJ0cXdBZfzQDwcJskaGayavbFvzSY=; 
 h=Message-ID:X-YMail-OSG:Received:X-Mailer:Date:From:Subject:To:MIME-Version:Content-Type; 
 b=gsz2P/9h3SL8PwBIU8g9dMGnHI+mwfSqjDhrkJljG3HNCbL0I4/DZJBGHnuqRwgpP4Rq0K3E/1z2Zwez4OhMI+naVzGg0P0ePG75GOwfXvl0XBvN8x8j1pgIOS8KQS24E1+6RogWIKJR2mht+KwiFK5uq3BYc9+nC9XMWsIE1fw=

Verifying a signature is going to require a public key. The public key is grabbed from a TXT record on the sender's the domain. The domain name is built with the "s" and "d" tags above. "s" + "._domainkey." + d = "s1024._domainkey.yahoo.com.au".

Using nslookup from the command prompt give us:

C:\>nslookup
Default Server:  DD-WRT
Address:  192.168.1.1

> set type=TXT
> s1024._domainkey.yahoo.com.au
Server:  DD-WRT
Address:  192.168.1.1

Non-authoritative answer:
s1024._domainkey.yahoo.com.au   text =

        "k=rsa; t=y; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrEee0Ri4Juz+QfiWYui/E9UGSXau/2P8LjnTD8V4Unn+2FAZVGE3kL23bzeoULYv4Pel
eB3gfm"
        "JiDJOKU3Ns5L4KJAUUHjFwDebt0NP+sBK0VKeTATL2Yr/S3bT/xhy+1xtj4RkdV7fVxTn56Lb4udUnwuxK4V5b5PdOKj/+XcwIDAQAB; n=A 1024 bit key
;"

The "b=" tag in DKIM-Signature is a signature of the canonicalized headers. The canonicalization process is done to make sure the signature is resilient to header modifications by mail servers. In this example we are using the 'relaxed' canonicalization algorithm. (Much more details about canonicalization in the RFC4871).

The original headers:

Message-ID: <88014.32082.qm@web112514.mail.gq1.yahoo.com>
X-YMail-OSG: aJFcCYUVM1mfioaf5zF7Pv_aQQ27DjD57ShG_QdRO6jTz5SGM4lVBkiJ1SD0tZmtvLEHhR4YtNFpTpI26b3VFERE44rlTAN3nP7a9.snzPvxcIIVOy6d0XWg1fvWS5bpOInMbv7M0bltJc6ml3gWPn8S3tT6U.Wz4OXu22meLo5VrikBRKeSGItA5Kw5Asn20GT84javGzv9_3znhG0HMejyHxpDjDbn.C_9OpKMVheG6HmBpgFVYmsW9q2IKc4r08YZvyADKNKnjN066fNhCNaYKWu3PEpHpbV4pZf66cFChegx94phIMQCI0cScc8ZQ5BLRj7qwV2F85C2ib22DWbZvTY-
Received: from [114.76.223.211] by web112514.mail.gq1.yahoo.com via HTTP; Thu, 13 Aug 2009 18:44:43 PDT
X-Mailer: YahooMailRC/1358.27 YahooMailWebService/0.7.338.2
Date: Thu, 13 Aug 2009 18:44:43 -0700 (PDT)
From: Russell Sayers 
Subject: from yahoo
To: diagnostic@smtp2web.com
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="0-1193717202-1250214283=:32082"
After relaxed canonicalization:
message-id:<88014.32082.qm@web112514.mail.gq1.yahoo.com>
x-ymail-osg:aJFcCYUVM1mfioaf5zF7Pv_aQQ27DjD57ShG_QdRO6jTz5SGM4lVBkiJ1SD0tZmtvLEHhR4YtNFpTpI26b3VFERE44rlTAN3nP7a9.snzPvxcIIVOy6d0XWg1fvWS5bpOInMbv7M0bltJc6ml3gWPn8S3tT6U.Wz4OXu22meLo5VrikBRKeSGItA5Kw5Asn20GT84javGzv9_3znhG0HMejyHxpDjDbn.C_9OpKMVheG6HmBpgFVYmsW9q2IKc4r08YZvyADKNKnjN066fNhCNaYKWu3PEpHpbV4pZf66cFChegx94phIMQCI0cScc8ZQ5BLRj7qwV2F85C2ib22DWbZvTY-
received:from [114.76.223.211] by web112514.mail.gq1.yahoo.com via HTTP; Thu, 13 Aug 2009 18:44:43 PDT
x-mailer:YahooMailRC/1358.27 YahooMailWebService/0.7.338.2
date:Thu, 13 Aug 2009 18:44:43 -0700 (PDT)
from:Russell Sayers 
subject:from yahoo
to:diagnostic@smtp2web.com
mime-version:1.0
content-type:multipart/alternative; boundary="0-1193717202-1250214283=:32082"

Now the canonicalized signature, minus the "b=" value. is placed after the headers. This had me scratching my head for a bit until I read this from the RFC:

The header field MUST be presented to the hash algorithm after the body of the message rather than with the rest of the header fields and MUST be canonicalized as specified in the "c=" (canonicalization) tag. The DKIM-Signature header field MUST NOT be included in its own h= tag..

We now have everything we need to verify the signature on the email using Bouncy Castle Crypto APIs

string base64pubkey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrEee0Ri4Juz+QfiWYui/E9UGSXau/2P8LjnTD8V4Unn+2FAZVGE3kL23bzeoULYv4PeleB3gfmJiDJOKU3Ns5L4KJAUUHjFwDebt0NP+sBK0VKeTATL2Yr/S3bT/xhy+1xtj4RkdV7fVxTn56Lb4udUnwuxK4V5b5PdOKj/+XcwIDAQAB";
RsaKeyParameters pubKey = PublicKeyFactory.CreateKey(Convert.FromBase64String(base64pubkey)) as RsaKeyParameters;
byte[] signature = Convert.FromBase64String("gsz2P/9h3SL8PwBIU8g9dMGnHI+mwfSqjDhrkJljG3HNCbL0I4/DZJBGHnuqRwgpP4Rq0K3E/1z2Zwez4OhMI+naVzGg0P0ePG75GOwfXvl0XBvN8x8j1pgIOS8KQS24E1+6RogWIKJR2mht+KwiFK5uq3BYc9+nC9XMWsIE1fw=");

string canonicalized =
@"message-id:<88014.32082.qm@web112514.mail.gq1.yahoo.com>
x-ymail-osg:aJFcCYUVM1mfioaf5zF7Pv_aQQ27DjD57ShG_QdRO6jTz5SGM4lVBkiJ1SD0tZmtvLEHhR4YtNFpTpI26b3VFERE44rlTAN3nP7a9.snzPvxcIIVOy6d0XWg1fvWS5bpOInMbv7M0bltJc6ml3gWPn8S3tT6U.Wz4OXu22meLo5VrikBRKeSGItA5Kw5Asn20GT84javGzv9_3znhG0HMejyHxpDjDbn.C_9OpKMVheG6HmBpgFVYmsW9q2IKc4r08YZvyADKNKnjN066fNhCNaYKWu3PEpHpbV4pZf66cFChegx94phIMQCI0cScc8ZQ5BLRj7qwV2F85C2ib22DWbZvTY-
received:from [114.76.223.211] by web112514.mail.gq1.yahoo.com via HTTP; Thu, 13 Aug 2009 18:44:43 PDT
x-mailer:YahooMailRC/1358.27 YahooMailWebService/0.7.338.2
date:Thu, 13 Aug 2009 18:44:43 -0700 (PDT)
from:Russell Sayers &lt;russell_sayers@yahoo.com.au&gt;
subject:from yahoo
to:diagnostic@smtp2web.com
mime-version:1.0
content-type:multipart/alternative; boundary=""0-1193717202-1250214283=:32082""
dkim-signature:v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com.au; s=s1024; t=1250214285; bh=cHSvmWBBhlnc7HYJ0cXdBZfzQDwcJskaGayavbFvzSY=; h=Message-ID:X-YMail-OSG:Received:X-Mailer:Date:From:Subject:To:MIME-Version:Content-Type; b=";
byte[] message = Encoding.ASCII.GetBytes(canonicalized);


ISigner sig = SignerUtilities.GetSigner("SHA256WithRSAEncryption");
sig.Init(false, pubKey);
sig.BlockUpdate(message, 0, message.Length);
if (sig.VerifySignature(signature))
{
    Console.WriteLine("all good!");
}

Labels: ,

Sunday, April 05, 2009 

Windows Azure - SlashView

Update (18-Sept-2009): Hot off twitter: Introducing the Windows Azure Service Management API - my wish (below) has been granted. :)

Here's my first attempt playing around with Azure: SlashView

The idea behind the site is pretty simple - I've got a worker process scraping all the comments off Slashdot every 10 minutes. I get all the stats off the comments, and display the results in a Silverlight graph.

The learning curve for Azure isn't too steep if you are already an ASP.NET developer. The only new technology I've had come up to speed with is the deployment process, and the table/blog storage services.

Some indispensable resources I've discovered along the way:

Wish list: an automated deploy. It can take a long while to 'Suspend', 'Delete', 'Deploy' from the developer portal..

Labels: ,