Een aantal dagen terug heeft Microsoft een sample gepost van een manier om met Windows Live ID te authenticeren op je site, zie Microsoft Connect. Hierbij leveren ze twee stukken code mee, een deel C# en een code in Ruby.
Allemaal heel leuk en aardig, maar ik heb niets aan Ruby, dat ondersteunt mijn server niet. Ik ben daarom bezig geweest om de boel te porten naar PHP, dit is vrij aardig gelukt, alleen is er één deel nog niet helemaal gelukt: het gedeelte waar encryptie bij komt kijken.
Wanneer je bent ingelogd (vanaf de Live ID-site) wordt er een formulier teruggepost naar je website (<form action=returnurl method=POST>), hierin is onder andere een securitytoken vervat. Dit token is gecodeerd met AES128-encryptie en een geheime key, ook bevat het token een signature.
Ik heb geprobeerd dit om te zetten naar PHP (ik gebruik o.a. mcrypt) maar hij geeft daarna een fout dat de signature niet overeenkomt. Ik vermoed dus dat mijn code niet helemaal goed is omgezet, echter weet ik niet waar de fout zit.
Op http://admin.ownedia.com/liveid staat een voorbeeld van de code, en op http://admin.ownedia.com/liveid/source staat mijn source. Je hebt - wil je het testen - een apart account nodig. Je kunt niet inloggen met je normale Windows Live-account, je hebt een apart Live-Int account nodig.
In de Ruby-code van Microsoft staan 3 functies die met encryptie te maken hebben:
Die heb ik omgezet naar deze code:
Ook is er een functie 'validateToken', die controleert of alles correct is:
(u64 is een procedure van MS die de ontvangen gegevens base64-decodeert)
Ergens hier zit dus een fout, maar ik weet niet waar. Heeft iemand van jullie toevallig een idee waar ik moet zoeken? Alvast vriendelijk bedankt
Allemaal heel leuk en aardig, maar ik heb niets aan Ruby, dat ondersteunt mijn server niet. Ik ben daarom bezig geweest om de boel te porten naar PHP, dit is vrij aardig gelukt, alleen is er één deel nog niet helemaal gelukt: het gedeelte waar encryptie bij komt kijken.
Wanneer je bent ingelogd (vanaf de Live ID-site) wordt er een formulier teruggepost naar je website (<form action=returnurl method=POST>), hierin is onder andere een securitytoken vervat. Dit token is gecodeerd met AES128-encryptie en een geheime key, ook bevat het token een signature.
Ik heb geprobeerd dit om te zetten naar PHP (ik gebruik o.a. mcrypt) maar hij geeft daarna een fout dat de signature niet overeenkomt. Ik vermoed dus dat mijn code niet helemaal goed is omgezet, echter weet ik niet waar de fout zit.
Op http://admin.ownedia.com/liveid staat een voorbeeld van de code, en op http://admin.ownedia.com/liveid/source staat mijn source. Je hebt - wil je het testen - een apart account nodig. Je kunt niet inloggen met je normale Windows Live-account, je hebt een apart Live-Int account nodig.
In de Ruby-code van Microsoft staan 3 functies die met encryptie te maken hebben:
Ruby:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
| def decodeToken(token) if (@cryptkey.nil? or @cryptkey.empty?) fatal("Error: decodeToken: Application secret was not set. Aborting.") end token = u64(token) if (token.nil? or (token.size <= 16) or !(token.size % 16).zero?) debug("Error: decodeToken: Attempted to decode invalid token.") return end iv = token[0..15] crypted = token[16..-1] begin aes128cbc = OpenSSL::Cipher::AES128.new("CBC") aes128cbc.decrypt aes128cbc.iv = iv aes128cbc.key = @cryptkey decrypted = aes128cbc.update(crypted) + aes128cbc.final rescue Exception => e debug("Error: decodeToken: Decryption failed: #{token}, #{e}") return end decrypted end def signToken(token) if (@signkey.nil? or @signkey.empty?) fatal("Error: signToken: Application secret was not set. Aborting.") end begin digest = OpenSSL::Digest::SHA256.new return OpenSSL::HMAC.digest(digest, @signkey, token) rescue Exception => e debug("Error: signToken: Signing failed: #{token}, #{e}") return end end def derive(secret, nonce) begin fatal("Nil/empty secret.") if (secret.nil? or secret.empty?) key = nonce + secret key = OpenSSL::Digest::SHA256.digest(key) return key[0..15] rescue Exception => e debug("Error: derive: #{e}") return end end |
Die heb ik omgezet naar deze code:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
| function decodeToken($token) { if(!isset($this->cryptKey) || $this->cryptKey == "") { die("Error: decodeToken: Application secret was not set. Aborting."); } $token = base64_decode(urldecode($token)); if(!isset($token) || strlen($token) < 16 || (strlen($token) % 16 != 0)) { die("Error: decodeToken: Attempted to decode invalid token."); } $iv = substr($token, 0, 16); $crypted = substr($token, 16, -1); $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->cryptKey, $crypted, "cbc", $iv); return $decrypted; } function signToken($token) { if(!isset($this->signKey) || $this->signKey == "") { die("Error: signToken: Application secret was not set. Aborting."); } $uitvoer = hash_hmac('sha256', $token, $this->signKey); echo $uitvoer; return $uitvoer; } function derive($secret, $nonce) { if(!isset($secret) || $secret == "") { die("Error: derive: Null/empty secret"); } try { $key = $nonce . $secret; $key = hash('sha256', $key); return substr($key, 0, 16); } catch (Exception $e) { die("Error: derive: ".$e); } } |
Ook is er een functie 'validateToken', die controleert of alles correct is:
Ruby:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| def validateToken(token) if (token.nil? or token.empty?) debug("Error: validateToken: nil/empty token.") return end body, sig = token.split("&sig=") if (body.nil? or sig.nil?) debug("Error: validateToken: Invalid token: #{token}") return end sig = u64(sig) return token if (sig == signToken(body)) debug("Error: validateToken: Signature did not match.") return end |
(u64 is een procedure van MS die de ontvangen gegevens base64-decodeert)
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| function validateToken($token) { if (!isset($token) || $token == "") { die("Error: validateToken: nil/empty token."); } $tokenArr = split("&sig=", $token); $body = $tokenArr[0]; $sig = $tokenArr[1]; if($body == "") { die("Error: validateToken: Invalid token: $token"); } $sig = base64_decode(urldecode($sig)); if($sig == $this->signToken($body)) { return $token; } else { die("Error: validateToken: Signature did not match."); } } |
Ergens hier zit dus een fout, maar ik weet niet waar. Heeft iemand van jullie toevallig een idee waar ik moet zoeken? Alvast vriendelijk bedankt
We are shaping the future