文字

hash_hmac

(PHP 5 >= 5.1.2, PECL hash >= 1.1)

hash_hmac使用 HMAC 方法生成带有密钥的哈希值

说明

string hash_hmac ( string $algo , string $data , string $key [, bool $raw_output = false ] )

参数

algo

要使用的哈希算法名称,例如:"md5","sha256","haval160,4" 等。 如何获取受支持的算法清单,请参见 hash_algos()

data

要进行哈希运算的消息。

key

使用 HMAC 生成信息摘要时所使用的密钥。

raw_output

设置为 TRUE 输出原始二进制数据, 设置为 FALSE 输出小写 16 进制字符串。

返回值

如果 raw_output 设置为 TRUE , 则返回原始二进制数据表示的信息摘要, 否则返回 16 进制小写字符串格式表示的信息摘要。 如果 algo 参数指定的不是受支持的算法,返回 FALSE

范例

Example #1 hash_hmac() 例程

<?php
echo  hash_hmac ( 'ripemd160' 'The quick brown fox jumped over the lazy dog.' 'secret' );
?>

以上例程会输出:

b8e7ae12510bdfb1812e463a7f086122cf37e4f7

参见

  • hash() - 生成哈希值 (消息摘要)
  • hash_algos() - 返回已注册的哈希算法列表
  • hash_init() - 初始化增量哈希运算上下文
  • hash_hmac_file() - 使用 HMAC 方法和给定文件的内容生成带密钥的哈希值

用户评论:

[#1] Michael [2013-02-19 20:52:21]

Please be careful when comparing hashes. In certain cases, information can be leaked by using a timing attack. It takes advantage of the == operator only comparing until it finds a difference in the two strings. To prevent it, you have two options.

Option 1: hash both hashed strings first - this doesn't stop the timing difference, but it makes the information useless.

<?php
    
if (md5($hashed_value) === md5($hashed_expected)) {
        echo 
"hashes match!";
    }
?>


Option 2: always compare the whole string.

<?php
    
if (hash_compare($hashed_value$hashed_expected)) {
        echo 
"hashes match!";
    }

    function 
hash_compare($a$b) {
        if (!
is_string($a) || !is_string($b)) {
            return 
false;
        }
        
        
$len strlen($a);
        if (
$len !== strlen($b)) {
            return 
false;
        }

        
$status 0;
        for (
$i 0$i $len$i++) {
            
$status |= ord($a[$i]) ^ ord($b[$i]);
        }
        return 
$status === 0;
    }
?>

[#2] pete dot walker at NOSPAM dot me dot com [2012-10-08 14:13:00]

A function implementing the algorithm outlined in RFC 6238 (http://tools.ietf.org/html/rfc6238)

<?php

function oauth_totp($key$time$digits=8$crypto='sha256')
{
    
$digits intval($digits);
    
$result null;
    
    
// Convert counter to binary (64-bit)       
    
$data pack('NNC*'$time >> 32$time 0xFFFFFFFF);
    
    
// Pad to 8 chars (if necessary)
    
if (strlen ($data) < 8) {
        
$data str_pad($data8chr(0), STR_PAD_LEFT);
    }        
    
    
// Get the hash
    
$hash hash_hmac($crypto$data$key);
    
    
// Grab the offset
    
$offset hexdec(substr($hashstrlen($hash) - 11));
    
    
// Grab the portion we're interested in
    
$binary hexdec(substr($hash$offset8)) & 0x7fffffff;
    
    
// Modulus
    
$result $binary pow(10$digits);
    
    
// Pad (if necessary)
    
$result str_pad($result$digits"0"STR_PAD_LEFT);
    
    return 
$result;
}
?>

[#3] relevant at celsius dot ee [2012-07-14 12:52:23]

<?php 

    
# replaces hash('tiger...
function hash_tiger_rev($algo$data$raw_output false) {
    
$len intval(substr($algo53)); # 128, 160 or 192 bits
    
$times substr($algo91); # 3 or 4
    
$revhash implode(""array_map("strrev"
        
str_split(hash('tiger192,'.$times$datatrue), 8)));
    if (
$len 192$revhash substr($revhash0$len >> 3);
    return 
$raw_output$revhashbin2hex($revhash);
}

# replaces hash_hmac('tiger...
function hash_hmac_tiger_rev($algo$data$key$raw_output false) {
    if (
strlen($key) > 64$key hash_tiger_rev($algo$key);
    
$key str_pad($key64chr(0));
    
$o_pad str_repeat("\\"64) ^ $key# "\" = chr(0x5C)
    
$i_pad str_repeat("6"64) ^ $key# "6" = chr(0x36)
    
return hash_tiger_rev($algo$o_pad 
           
hash_tiger_rev($algo$i_pad $datatrue), $raw_output);
}

# always the new version of tiger
function hash_hmac_new($algo$data$key$raw_output false) {
    if (
phpversion() > '5.4' || !preg_match('/^tiger(128|160|192),(3|4)$/'$algo)) 
        return 
hash_hmac($algo$data$key$raw_output);
    else
        return 
hash_hmac_tiger_rev($algo$data$key$raw_output);
}

# always the old version of tiger
function hash_hmac_old($algo$data$key$raw_output false) {
    if (
phpversion() < '5.4' || !preg_match('/^tiger(128|160|192),(3|4)$/'$algo)) 
        return 
hash_hmac($algo$data$key$raw_output);
    else
        return 
hash_hmac_tiger_rev($algo$data$key$raw_output);
}

# let's test it
$algo 'tiger160,4'$pwd 'foo'$key 'bar';
echo 
hash_hmac($algo$pwd$key), "<br>";
echo 
hash_hmac_tiger_rev($algo$pwd$key), "<br>"
echo 
"<br>";
echo 
hash_hmac_old($algo$pwd$key), "<br>";
echo 
hash_hmac_new($algo$pwd$key), "<br>";



?>

[#4] havoc at NOSPAM defuse dot ca [2012-06-30 16:30:00]

Here is an efficient PBDKF2 implementation:

<?php

function pbkdf2($algorithm$password$salt$count$key_length$raw_output false)
{
    
$algorithm strtolower($algorithm);
    if(!
in_array($algorithmhash_algos(), true))
        die(
'PBKDF2 ERROR: Invalid hash algorithm.');
    if(
$count <= || $key_length <= 0)
        die(
'PBKDF2 ERROR: Invalid parameters.');

    
$hash_length strlen(hash($algorithm""true));
    
$block_count ceil($key_length $hash_length);

    
$output "";
    for(
$i 1$i <= $block_count$i++) {
        
// $i encoded as 4 bytes, big endian.
        
$last $salt pack("N"$i);
        
// first iteration
        
$last $xorsum hash_hmac($algorithm$last$passwordtrue);
        
// perform the other $count - 1 iterations
        
for ($j 1$j $count$j++) {
            
$xorsum ^= ($last hash_hmac($algorithm$last$passwordtrue));
        }
        
$output .= $xorsum;
    }

    if(
$raw_output)
        return 
substr($output0$key_length);
    else
        return 
bin2hex(substr($output0$key_length));
}
?>

[#5] martin dot papik at ipsec dot info [2012-06-09 07:38:58]

Yet another OATH HOTP function. Has a 64 bit counter and is a lot shorter. Enjoy.

<?php

function oath_hotp ($secret$ctr$len=6) {
        
$binctr pack ('NNC*'$ctr>>32$ctr 0xFFFFFFFF);
        
$hash hash_hmac ("sha1"$binctr$secret);
// This is where hashing stops and truncation begins
        
$ofs 2*hexdec (substr ($hash391));
        
$int hexdec (substr ($hash$ofs8)) & 0x7FFFFFFF;
        
$pin substr ($int, -$len);
        
$pin str_pad ($pin$len"0"STR_PAD_LEFT);
        return 
$pin;
}

?>

[#6] josefkoh at hotmail dot com [2011-07-27 10:24:07]

Simple implementation of hmac sha1

<?php

function hmac_sha1($key, $data)
{
    // Adjust key to exactly 64 bytes
    if (strlen($key) > 64) {
        $key = str_pad(sha1($key, true), 64, chr(0));
    }
    if (strlen($key) < 64) {
        $key = str_pad($key, 64, chr(0));
    }

    // Outter and Inner pad
    $opad = str_repeat(chr(0x5C), 64);
    $ipad = str_repeat(chr(0x36), 64);

    // Xor key with opad & ipad
    for ($i = 0; $i < strlen($key); $i++) {
        $opad[$i] = $opad[$i] ^ $key[$i];
        $ipad[$i] = $ipad[$i] ^ $key[$i];
    }

    return sha1($opad.sha1($ipad.$data, true));
}

[#7] Peter Terence Roux [2010-12-23 03:44:48]

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$salt1032'sha1');
  
$p base64_encode($p);

  
$iv str_hash_pbkdf2($pw$salt1016'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), $ptrue);

      
// Perform block iterations
      
for ($i=1$i<$c$i++)
      {
        
// XOR each iterate
        
$ib ^= ($h hash_hmac($a$h$ptrue));
      }

      
$dk .= $ib;                                // Append iterated block
    
}

    
// Return derived key of correct length
    
return substr($dk$start$kl);
  }
?>

[#8] Siann Beck [2010-10-10 09:48:32]

For signing an Amazon AWS query, base64-encode the binary value:

<?php
  $Sig 
base64_encode(hash_hmac('sha256'$Request$AmazonSecretKeytrue));
?>

[#9] KC Cloyd [2009-09-09 23:16:30]

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)), $sizechr(0x00));
    } else {
        
$key str_pad($key$sizechr(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);

?>

[#10] brent at thebrent dot net [2009-05-21 08:17:56]

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), 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) << ) |
        (
$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";
}
?>

[#11] torben dot egmose at gmail dot com [2009-03-22 11:40:43]

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";

[#12] Carlos Averett(caverett*@*corecodec,net) [2008-07-03 15:54:52]

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), 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 ($hash38)) & 0xf;

        
// Extract the relevant part, and clear the first bit
        
$hex_truncated substr ($hash$offset 28);
        
$bin_truncated decbin (hexdec ($hex_truncated));
        
$bin_truncated[0] = '0';
        
$dec_truncated bindec ($bin_truncated);

        return 
substr ($dec_truncated$length);
}
?>

上一篇: 下一篇: