MENU

HMAC散列消息认证码

August 29, 2017 • PHP

在开发一个开发接口的项目中用到了接口签名验证,有机会了解到了HMAC。关于HMAC的定义先来一段维基百科

密钥散列消息认证码(英语:Keyed-hash message authentication code,缩写为HMAC),又称散列消息认证码(Hash-based message authentication code),是一种通过特别计算方式之后产生的消息认证码(MAC),使用密码散列函数,同时结合一个加密密钥。它可以用来保证数据的完整性,同时可以用来作某个消息的身份验证。

HMAC与传统散列函数对比

传统的验签方式我们一般都是采用的md5或者sha家族函数(sha1、sha256、sha512等)这样的散列函数。而HMAC并不是一种散列函数,而是采用了将MD5或SHA1散列函数与共享机密秘钥(与公钥/秘钥对不同)一起使用的消息身份验证机制,消息与秘钥组合并运行散列函数(md5或sha1),然后运行结果与秘钥组合并再次运行散列函数。到这里可以简单的理解为使用md5或者是sha家族函数与HMAC机制相结合生成更加难以破解的加密串(消息摘要)。

在PHP中使用hash_hmac函数进行生成,原型如下

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

示例

$key = 'abcxyz';
$message = '被加密的内容';

$val = hash_hmac('sha256', $message, $key);
# output
980ad1f49d53baa814660c6bf29ccc4a8fc63e4a11678ef716585e431cb582d6
#php手册 对输入的二进制做base64加密
$sign = base64_encode(hash_hmac('sha256', $Request, $AmazonSecretKey, true));

应用实践

一般在做接口签名的时候接口提供商会会分配给应用2个密钥,简称AK和SK。应用在请求接口的时候都会被要求传递一个签名(sign)。那么一般会把请求参数转换成一个字符串($signString)然后使用下面的方法签名

$sign = hash_hmac('sha256', $signString, $SK);

然后服务端也会根据同样的算法生成一个签名串与氪户端传递的签名串进行比对。

参考 http://blog.csdn.net/dengjiexian123/article/details/53313913
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3

一个可以当作短信验证码的函数

/**
 * 基于时间的一次性口令算法
 * This function implements the algorithm outlined
 * in RFC 6238 for Time-Based One-Time Passwords
 *
 * @link http://tools.ietf.org/html/rfc6238
 * @param string $key    the string to use for the HMAC key
 * @param mixed  $time   a value that reflects a time (unix
 *                       time in the example)
 * @param int    $digits the desired length of the OTP
 * @param string $crypto the desired HMAC crypto algorithm
 * @return string the generated OTP
 */
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($data, 8, chr(0), STR_PAD_LEFT);
    }        
    
    // Get the hash
    $hash = hash_hmac($crypto, $data, $key);
    
    // Grab the offset
    $offset = 2 * hexdec(substr($hash, strlen($hash) - 1, 1));
    
    // Grab the portion we're interested in
    $binary = hexdec(substr($hash, $offset, 8)) & 0x7fffffff;
    
    // Modulus
    $result = $binary % pow(10, $digits);
    
    // Pad (if necessary)
    $result = str_pad($result, $digits, "0", STR_PAD_LEFT);
    
    return $result;
}


echo oauth_totp($key, time(), 6); #output 725557
Last Modified: November 10, 2019