Při psaní přihlašování, kde jsem chtěl používat hmac, aby se neposílalo heslo v plain textu, jsem si náhodou všiml, že mi nesedí výsledný hash_hmac, když se v hesle vyskytne znak specifický pro českou abecedu. Prekérní situace… 😎
Drobný příklad, mějme řetězec a klíč.
- řetězec:
- ščřžýáá je to na nic 123456
- klíč:
- TLr8zA75tI1q3Jo
php (běžně používané funkce):
hash(sha256) = 8af726e3c15be3687de0b13102667701994323832bb252503f841bf3d3722e34 hash_hmac(sha256) = d39a1c610d009f1f1f569800fc4d74ac8d27a8b8e2011b526bf886f7a066fec1
jquery plugin pro sha256 (jquery.sha256.min.js):
hash(sha256) = 723f6dd8d04fe3ac3f268f1d6b9a938a778ad71d6f699de968206ad4c5909bdf hash_hmac(sha256) = 83a2bdbd3ba93279fa94c76d31ed506d132f167fc46e4dd203b84ea7e861cf1a
Problém je na světě, ono to nesedí. Hledal jsem i jinou implementaci a přes Wikipedii našel…
javascript (implementace například z http://jssha.sourceforge.net/):
hash(sha256) = 723f6dd8d04fe3ac3f268f1d6b9a938a778ad71d6f699de968206ad4c5909bdf hash_hmac(sha256) = 83a2bdbd3ba93279fa94c76d31ed506d132f167fc46e4dd203b84ea7e861cf1a
Opět jsou výsledky různé. Stejně jsem kvůli „českým znakům“ potřeboval pracovat s řetězci v Unicodu a ne ASCII, jak nabízí php u funkcí hash a hash_hmac. A hlavně jsem potřeboval v javascriptové i phpčkové části dostávat stejné výsledky.
Implementace sha256 pro php, která umí pracovat s Unicode, se dala najít na internetu celkem snadno – zde, takže stačilo jen ověřit chování. Nastavit pro práci s Unicode ($chrsz = 16;) a doplnit o hmac:
function muj_sha256_hmac($key, $data) { $blocksize = 64; if (strlen($key) > $blocksize) { $key = muj_sha256($key); } else if (strlen($key) < $blocksize) { $key = str_pad($key, $blocksize, chr(0x00)); } $ipad = $key ^ str_repeat(chr(0x36), $blocksize); $opad = $key ^ str_repeat(chr(0x5C), $blocksize); return muj_sha256($opad . pack('H*', muj_sha256($ipad . $data))); }
U javascriptu se muselo udělat trochu více, ale analogicky podle phpčkového skriptu. Poznámka jen pro budoucnost, až budu trojčit, že mi nesedí hashe :-).
Doplnění 1:
Všechno fungovalo krásně, dokud nedošlo na 64bitový server, protože pak jsem obdržel opět různé údaje. Přitom již minule stačilo php část vyřešit za pomoci mb_convert_encoding() a převést text na UTF-16LE.
hash('sha256', mb_convert_encoding('text v SHA256', 'UTF-16LE'));
Napsat komentář