I’ve been working at this years Q Awards.
Look out for content i’m posting via mobile and wireless on the official website, q4music.com.
I’ve been working at this years Q Awards.
Look out for content i’m posting via mobile and wireless on the official website, q4music.com.
Those of you in the UK and using Nokia Lifeblog on Vodafone contract may have noticed it no longer works.
We have problems at the moment with Vodafone’s new mobile optimising technology. This takes a page and tries to fit it onto a mobile phone screen. It does this by pretending to be a more competant browser in it’s HTTP headers.
However, in faking the HTTP headers it also strips out the WSSE authentication that Lifeblog uses for it’s security, meaning that each post will fail as unauthorised.
There is currently no easy work around for this except to change network or to register your site with Bango as they are apparently whitelisting sites to bypass this new proxy.
I received an email asking for a hand with a C# .NET implementation of the WSSE atom authentication for Nokia’s Lifeblog.
I took a look at this as I’m brushing up on my C# at the moment for work, and I relish a challenge.
As there are two different methods of authentication depending on the version of Lifeblog being used, the code has to handle this.
The first method is to append the nonce, timestamp and password together, create a SHA1 hash with this and Base64 encode the result. The second way is almost the same, but the nonce has to be Base64 decoded first before being appended with the timestamp and password.
See my previous article on WSSE Authentication For Atom Using Perl for more details on how this works.
The first method can be calculate a digest value using code like this.
string mystring = nonce + created + password;
SHA1Managed SHhash = new SHA1Managed();
string mydigest = Convert.ToBase64String(SHhash.ComputeHash(System.Text.Encoding.ASCII.GetBytes(mystring)));
My first attempt at the second method looked very similar.
string mystring = (System.Text.Encoding.ASCII.GetString(Convert.FromBase64String(nonce))) + created + password;
SHA1Managed SHhash = new SHA1Managed();
string mydigest = Convert.ToBase64String(SHhash.ComputeHash(System.Text.Encoding.ASCII.GetBytes(mystring)));
Looks OK on the surface, but it fails to validate when posting from my Nokia N93 phone.
What’s gone wrong?
Well looking in the debugger, the process of converting the nonce back from the array of bytes Convert.FromBase64String()
produces and appending them to form mystring corrupts the data compared with the Perl version in my previous article.
We know the approach is basically right, so we need to cut out this corrupting step.
The best way to do this is to keep the data in byte arrays as we need the data in this format anyway to computer the SHA1.
System.Text.Encoding enc = System.Text.Encoding.UTF8;
byte[] noncebytes = enc.GetBytes(nonce);
byte[] passwordbytes = enc.GetBytes(password);
byte[] createdbytes = enc.GetBytes(created);
byte[] code = new byte[nonce.Length + password.Length + created.Length];
Array.Copy(nonce, code, nonce.Length);
Array.Copy(created, 0, code, nonce.Length, created.Length);
Array.Copy(password, 0, code, nonce.Length + created.Length, password.Length);
System.Security.Cryptography.SHA1Managed SHhash = new System.Security.Cryptography.SHA1Managed();
string digest = Convert.ToBase64String(SHhash.ComputeHash(code));
I’m sure there is a nicer way to achieve the appending of the 3 arrays together in C#, but this works for now.
We can tie this up to form a nice static class to handle our WSSE authentication.
/// <summary>
/// Computes WSSE codes from input for validation.
/// </summary>
public static class WSSE {
/// <summary>
/// Calculate the digest.
/// </summary>
/// <param name="password">A string with your unencrypted password.</param>
/// <param name="nonce">The nonce the digest is to be created with as a string.</param>
/// <param name="created">The timestamp as a string.</param>
/// <returns>A string containing the digest.</returns>
public static string GetDigest(string password, string nonce, string created)
{
return GetDigest(password, nonce, created, System.Text.Encoding.ASCII);
}
/// <summary>
/// Calculate the digest.
/// </summary>
/// <param name="password">A string with your unencrypted password.</param>
/// <param name="nonce">The nonce the digest is to be created with as a string.</param>
/// <param name="created">The timestamp as a string.</param>
/// <param name="enc">The System.Text.Encoding to use.</param>
/// <returns>A string containing the digest.</returns>
public static string GetDigest(string password, string nonce, string created, System.Text.Encoding enc)
{
byte[] noncebytes = enc.GetBytes(nonce);
byte[] passwordbytes = enc.GetBytes(password);
byte[] createdbytes = enc.GetBytes(created);
return generatecode(noncebytes, passwordbytes, createdbytes);
}
/// <summary>
/// Calculate the alternative digest.
/// </summary>
/// <param name="password">A string with your unencrypted password.</param>
/// <param name="nonce">The nonce the digest is to be created with as a string.</param>
/// <param name="created">The timestamp as a string.</param>
/// <returns>A string containing the digest.</returns>
public static string GetDigestAlt(string password, string nonce, string created)
{
return GetDigestAlt(password, nonce, created, System.Text.Encoding.ASCII);
}
/// <summary>
/// Calculate the alternative digest.
/// </summary>
/// <param name="password">A string with your unencrypted password.</param>
/// <param name="nonce">The nonce the digest is to be created with as a string.</param>
/// <param name="created">The timestamp as a string.</param>
/// <param name="enc">The System.Text.Encoding to use.</param>
/// <returns>A string containing the digest.</returns>
public static string GetDigestAlt(string password, string nonce, string created, System.Text.Encoding enc)
{
byte[] noncebytes = Convert.FromBase64String(nonce);
byte[] passwordbytes = enc.GetBytes(password);
byte[] createdbytes = enc.GetBytes(created);
return generatecode(noncebytes, passwordbytes, createdbytes);
}
private static string generatecode(byte[] nonce, byte[] password, byte[] created)
{
byte[] code = new byte[nonce.Length + password.Length + created.Length];
Array.Copy(nonce, code, nonce.Length);
Array.Copy(created, 0, code, nonce.Length, created.Length);
Array.Copy(password, 0, code, nonce.Length + created.Length, password.Length);
System.Security.Cryptography.SHA1Managed SHhash = new System.Security.Cryptography.SHA1Managed();
return Convert.ToBase64String(SHhash.ComputeHash(code));
}
/// <summary>
/// Validates password, nonce and created create the same digest code as digest.
/// </summary>
/// <param name="password">A string with your unencrypted password.</param>
/// <param name="nonce">The nonce the digest is to be created with as a string.</param>
/// <param name="created">The timestamp as a string.</param>
/// <param name="digest">The digest to validate the password, nonce and created strings against as a string.</param>
/// <returns>true or false depending on wether the digest validates</returns>
public static bool IsValid(string password, string nonce, string created, string digest)
{
return digest == GetDigest(password, nonce, created) || digest == GetDigestAlt(password, nonce, created);
}
/// <summary>
/// Validates password, nonce and created create the same digest code as digest.
/// </summary>
/// <param name="password">A string with your unencrypted password.</param>
/// <param name="nonce">The nonce the digest is to be created with as a string.</param>
/// <param name="created">The timestamp as a string.</param>
/// <param name="digest">The digest to validate the password, nonce and created strings against as a string.</param>
/// <param name="enc">A System.Text.Encoding encoding.</param>
/// <returns>true or false depending on wether the digest validates.</returns>
public static bool IsValid(string password, string nonce, string created, string digest, System.Text.Encoding enc)
{
return digest == GetDigest(password, nonce, created, enc) || digest == GetDigestAlt(password, nonce, created, enc);
}
}
We can test the code using the following code snippet.
string digest = "nvvHvNuLb+7wGFrop+cC2tjgQqs=";
string nonce = "bgZ4BHcjmWcg7gVhCxyQOg==";
string timestamp = "2006-04-25T19:40:45Z";
string password = "a";
if (WSSE.IsValid(password, nonce, timestamp, digest, System.Text.Encoding.UTF8))
{
Console.WriteLine("valid");
}
else
{
Console.WriteLine("invalid");
}
This gives us the result “valid” as we’d hope.
To save you time, feel free to download the C# code
WSSE.cs.
Nokia seem to have updated their Atom upload protocol in recent versions of their phones.
I’ve just got hold of a Nokia N93 and tried posting to this blog using Lifeblog and the new Web Upload functionality in the Gallery with use the Atom protocol.
These postings were failing with a bad password error. I know my username and password are correct and they worked using my old phone.
As I had written my own blogging software, and it’s Atom upload functionality I was able to debug the messages being sent from the phone to the server.
It turns out that newer versions of Nokia Lifeblog encrypt their passwords differently to older versions.
They use a method called WSSE. See my previous article on how to use WSSE with Perl for more details on how this works, plus example code.
Very quickly, the password isn’t sent, but a digest of the password and the values to generate the digest yourself are. These are then encoded using base64 for transmission over the internet.
Older versions (pre Lifeblog 2.0) expect a value called the nonce to be used as sent while the password digest is generated. Newer versions (Lifeblog 2.0+) expect the nonce to be decoded from base64 before the digest is generated.
Once I had worked out what was going on, it was simple to modify my code to check against both possible versions of the password digest. This means I can now post using old and new versions of Lifeblog, or the web upload functionality.
Here’s a very quick bit of example Perl code. Assume $my_digest is the digest with the original nonce left in place and $my_alternative_digest is the digest with the nonce decoded first. $digest is the digest sent my incoming Atom request.
## example perl code to check digests)
if (($digest eq $my_digest) || ($digest eq $my_alternative_digest)) {
## one of the digests has validated, so continue here
} else {
## neither digests has validated, so return invalid password responses here.
}
I don’t think this change has been communicated very well amongst the development community. It has probably come about due to changes in the Atom spec, but (admittedly only quickly) checking the documentation I’ve not found it.
I will be updating my WSSE validation example accordingly, and a new version of AtomServer.pm that allows posting from Lifeblog 2.0 to Movable Type shortly.
Here’s a test shot of my bookshelves from a Nokia N93 and posted using Nokia Lifeblog.
We’ve been installing Movable Type as a blogging solution at work for various sites recently.
We’d been using Typepad, the hosted version of Movable Type for a while, but wanted some extra flexibility and functionality.
One problem we came across was the lack of support for Nokia Lifeblog on Movable Type, compared to Typepad.
Thankfully Martin Higham has worked on this in the past, and even used my old notes on the Nokia Lifeblog Posting Protocol.
However, the current version of Movable Type is 3.3, and Martin’s work only extends to 3.2.
I took Martin’s code, which modifies AtomServer.pm
and modified it so it works on Movable Type 3.3. You can download my Nokia Lifeblog compatible AtomServer.pm for Movable Type 3.3 here.
Make sure you follow Martin’s instructions for Movable Type 3.2 as the method is exactly the same and the same caveats apply (namely, this could well break other Atom tools that use your blog as it has to disable some WSSE authentication).
I hope you find it useful!
UPDATE: 22nd November 2006
With version 2.0 of Lifeblog and web upload functionality from Nokia phones, the authentication method has changed slightly. A new version of AtomServer.pm that works with Lifeblog 2.0 is now available.
UPDATE: 26th June 2007
Neils Berkers has been in touch to say he’s adapted the script so it now works with Movable Type 3.35, Lifeblogging Fixed.
Now here’s an interesting bug in Nokia Lifeblog application on my Nokia 3230 phone.
If you take a photo using the camera and then post it to the web using Lifeblog, it won’t then let you send the image via MMS. Instead, it complains “unable to send copyright protected item”, even though I own the copyright of my own picture.
This is running version 1.51.2 of the Lifeblog software.
Charlie has a photo of his back garden on his blog that was updated using the Flickr uploader on his Nokia N93 phone.
FYI, the ‘Flickr’ uploader is using the same protocol as Lifeblog. So, if you know how that is done, you can theoretically post to other Lifeblog compatible sites.
That’s really interesting, and I was pondering which protocol the Flickr uploader would use. Lifeblog uses the Atom protocol, and I have written code to take lifeblog entries and upload them to a blog in the past.
I just need to get hold of a nice new Nokia N93 phone to test my code still works with the new Flickr uploader.
It seems we’re not allowed outside.
It’s eerie walking through London trying to get home today.
As Liverpool Street is obviously shut following the terrorist bombings, i decided to try going to Eastbourne from Victoria instead.
Buckingham Palace is partially sealed off, and armed soldiers in combat gear are patrolling instead of the usual dress sentries standing outside.
As buses are suspended, the bus station at Victoria was deserted. It was also strange seeing Oxford Street without a single bus running down it.
Trains are a nightmare. I’m on a special Brighton service as very few trains are running. There are no tubes or buses running so it’s very busy.
Strangely, despite all the trains being messed up, the public information board cheerfully informs us there are no reported problems today, and tries to sell us balloon rides, gay pride and Party in the Park tickets.