(PHP 5 >= 5.1.2, PECL hash >= 1.1)
hash_hmac — Berechnet einen Hash mit Schlüssel unter Verwendung von HMAC
Name des gewählten Hash-Algorithmus (z.B. "md5", "sha256", "haval160,4", usw...). Für eine Liste der unterstützten Algorithmen, siehe hash_algos().
Nachricht, die gehasht werden soll.
Geheimer Schlüssel für die Berechnung der HMAC-Variante des Hashs.
Ist dieser Parameter TRUE, werden direkt Binärdaten zurückgegeben, andernfalls werden klein geschriebene Hexadezimalziffern zurückgegeben.
Gibt den berechneten Hash als Hexadezimalzahl zurück, außer raw_output ist wahr, in diesem Fall wird die binäre Darstellung des Hashes zurückgegeben.
Beispiel #1 hash_hmac()-Beispiel
<?php
echo hash_hmac('ripemd160', 'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern.', 'geheimnis');
?>
Das oben gezeigte Beispiel erzeugt folgende Ausgabe:
2c74049f7b3ce927cefaaa4162c98abe234f971f
The Implementation of the PBKDF2 key derivation function as described in RFC 2898 can be used to not only get the hashed KEY but also a specific IV.
To use, one would use it as follows:-
<?php
$p = str_hash_pbkdf2($pw, $salt, 10, 32, 'sha1');
$p = base64_encode($p);
$iv = str_hash_pbkdf2($pw, $salt, 10, 16, 'sha1', 32);
$iv = base64_encode($iv);
?>
The function should be:-
<?php
// PBKDF2 Implementation (described in RFC 2898)
//
// @param string p password
// @param string s salt
// @param int c iteration count (use 1000 or higher)
// @param int kl derived key length
// @param string a hash algorithm
// @param int st start position of result
//
// @return string derived key
function str_hash_pbkdf2($p, $s, $c, $kl, $a = 'sha256', $st=0)
{
$kb = $start+$kl; // Key blocks to compute
$dk = ''; // Derived key
// Create key
for ($block=1; $block<=$kb; $block++)
{
// Initial hash for this block
$ib = $h = hash_hmac($a, $s . pack('N', $block), $p, true);
// Perform block iterations
for ($i=1; $i<$c; $i++)
{
// XOR each iterate
$ib ^= ($h = hash_hmac($a, $h, $p, true));
}
$dk .= $ib; // Append iterated block
}
// Return derived key of correct length
return substr($dk, $start, $kl);
}
?>
For signing an Amazon AWS query, base64-encode the binary value:
<?php
$Sig = base64_encode(hash_hmac('sha256', $Request, $AmazonSecretKey, true));
?>
Sometimes a hosting provider doesn't provide access to the Hash extension. Here is a clone of the hash_hmac function you can use in the event you need an HMAC generator and Hash is not available. It's only usable with MD5 and SHA1 encryption algorithms, but its output is identical to the official hash_hmac function (so far at least).
<?php
function custom_hmac($algo, $data, $key, $raw_output = false)
{
$algo = strtolower($algo);
$pack = 'H'.strlen($algo('test'));
$size = 64;
$opad = str_repeat(chr(0x5C), $size);
$ipad = str_repeat(chr(0x36), $size);
if (strlen($key) > $size) {
$key = str_pad(pack($pack, $algo($key)), $size, chr(0x00));
} else {
$key = str_pad($key, $size, chr(0x00));
}
for ($i = 0; $i < strlen($key) - 1; $i++) {
$opad[$i] = $opad[$i] ^ $key[$i];
$ipad[$i] = $ipad[$i] ^ $key[$i];
}
$output = $algo($opad.pack($pack, $algo($ipad.$data)));
return ($raw_output) ? pack($pack, $output) : $output;
}
?>
Example Use:
<?php
custom_hmac('sha1', 'Hello, world!', 'secret', true);
?>
<?php
/**
* Implementation of the PBKDF2 key derivation function as described in RFC 2898.
*
* PBKDF2 was published as part of PKCS #5 v2.0 by RSA Security. The standard is
* also documented in IETF RFC 2898.
*
* The first four function arguments are as the standard describes:
*
* PBKDF2(P, S, c, dkLen)
*
* The fifth function argument specifies the hash function to be used. This should
* be provided in the same format as used for the hash() function. The default
* hash algorithm is SHA-1, but this is not recommended for new applications.
*
* The function returns false if dk_len is too large. Otherwise it returns the
* derived key as a binary string.
*
* @author Henry Merriam <php@henrymerriam.com>
*
* @param string p password
* @param string s salt
* @param int c iteration count
* @param int dk_len derived key length (octets)
* @param string algo hash algorithm
*
* @return string derived key
*/
function pbkdf2($p, $s, $c, $dk_len, $algo = 'sha1') {
// experimentally determine h_len for the algorithm in question
static $lengths;
if (!isset($lengths[$algo])) { $lengths[$algo] = strlen(hash($algo, null, true)); }
$h_len = $lengths[$algo];
if ($dk_len > (pow(2, 32) - 1) * $h_len) {
return false; // derived key is too long
} else {
$l = ceil($dk_len / $h_len); // number of derived key blocks to compute
$t = null;
for ($i = 1; $i <= $l; $i++) {
$f = $u = hash_hmac($algo, $s . pack('N', $i), $p, true); // first iterate
for ($j = 1; $j < $c; $j++) {
$f ^= ($u = hash_hmac($algo, $u, $p, true)); // xor each iterate
}
$t .= $f; // concatenate blocks of the derived key
}
return substr($t, 0, $dk_len); // return the derived key of correct length
}
}
?>
The hotp algorithms above work with counter values less than 256, but since the counter can be larger, it's necessary to iterate through all the bytes of the counter:
<?php
function oath_hotp ($key, $counter)
{
// Counter
//the counter value can be more than one byte long, so we need to go multiple times
$cur_counter = array(0,0,0,0,0,0,0,0);
for($i=7;$i>=0;$i--)
{
$cur_counter[$i] = pack ('C*', $counter);
$counter = $counter >> 8;
}
$bin_counter = implode($cur_counter);
// Pad to 8 chars
if (strlen ($bin_counter) < 8)
{
$bin_counter = str_repeat (chr(0), 8 - strlen ($bin_counter)) . $bin_counter;
}
// HMAC
$hash = hash_hmac ('sha1', $bin_counter, $key);
return $hash;
}
function oath_truncate($hash, $length = 6)
{
// Convert to dec
foreach(str_split($hash,2) as $hex)
{
$hmac_result[]=hexdec($hex);
}
// Find offset
$offset = $hmac_result[19] & 0xf;
// Algorithm from RFC
return
(
(($hmac_result[$offset+0] & 0x7f) << 24 ) |
(($hmac_result[$offset+1] & 0xff) << 16 ) |
(($hmac_result[$offset+2] & 0xff) << 8 ) |
($hmac_result[$offset+3] & 0xff)
) % pow(10,$length);
}
print "<pre>";
print "Compare results with:";
print " http://tools.ietf.org/html/draft-mraihi-oath-hmac-otp-04\n";
print "Count\tHash\t\t\t\t\t\tPin\n";
for($i=0;$i<=1024;$i=$i+128)
{
print $i."\t".($a=oath_hotp("12345678901234567890",$i));
print "\t".oath_truncate($a)."\n";
}
?>
HOTP Algorithm that works according to the RCF http://tools.ietf.org/html/draft-mraihi-oath-hmac-otp-04
The test cases from the RCF document the ASCII string as "123456787901234567890".
But the hex decoded to a string is "12345678901234567890".
Secret="12345678901234567890";
Count:
0 755224
1 287082
<?php
function oath_hotp($key,$counter) {
// Convert to padded binary string
$data = pack ('C*', $counter);
$data = str_pad($data,8,chr(0),STR_PAD_LEFT);
// HMAC
return hash_hmac('sha1',$data,$key);
}
function oath_truncate($hash, $length = 6) {
// Convert to dec
foreach(str_split($hash,2) as $hex) {
$hmac_result[]=hexdec($hex);
}
// Find offset
$offset = $hmac_result[19] & 0xf;
// Algorithm from RFC
return (
(($hmac_result[$offset+0] & 0x7f) << 24 ) |
(($hmac_result[$offset+1] & 0xff) << 16 ) |
(($hmac_result[$offset+2] & 0xff) << 8 ) |
($hmac_result[$offset+3] & 0xff)
) % pow(10,$length);
}
print "<pre>";
print "Compare results with:"
print " http://tools.ietf.org/html/draft-mraihi-oath-hmac-otp-04\n";
print "Count\tHash\t\t\t\t\t\tPin\n";
for($i=0;$i<10;$i++)
print $i."\t".($a=oath_hotp("12345678901234567890",$i))
print "\t".oath_truncate($a)."\n";
Generating OATH-compliant OTP (one time passwords) results in PHP:
<?php
$otp = oath_truncate (oath_hotp ($key, $counter), $length);
function oath_hotp ($key, $counter) {
// Counter
$bin_counter = pack ('C*', $counter);
// Pad to 8 chars
if (strlen ($bin_counter) < 8) {
$bin_counter = str_repeat (chr(0), 8 - strlen ($bin_counter)) . $bin_counter;
}
// HMAC
$hash = hash_hmac ('sha1', $bin_counter, $key);
return $hash;
}
function oath_truncate ($hash, $length = 6) {
// The last byte is used as an offset
$offset = hexdec (substr ($hash, 38)) & 0xf;
// Extract the relevant part, and clear the first bit
$hex_truncated = substr ($hash, $offset * 2, 8);
$bin_truncated = decbin (hexdec ($hex_truncated));
$bin_truncated[0] = '0';
$dec_truncated = bindec ($bin_truncated);
return substr ($dec_truncated, 0 - $length);
}
?>