php使用hash算法加解密和签名验签

2021-09-11
/**
 * @description: hash加密、签名字符串和文件,文件支持本地路径(以盘符或/开头)、url(http或https开头)和fopen的资源
 */
class HashAlgorithm
{

    public $algorithm;

    /**
     * @description: 判断是否为文件
     * @param {mixed} $data
     * @return {*}
     */
    private function _isFile(mixed $data):bool
    {
        // 是字符串且以盘符或/或http开头
        if (is_string($data) && ($data[0] == '/' || strpos($data[0], 'http') === 0 || preg_match('/^[a-zA-Z]\:/', $data) > 0)) {
            return true;
        } elseif (is_resource($data)) {
            return true;
        }
        return false;
    }

    /**
     * @description: hash加密
     * @param {string} $algo 算法名称,支持算法:hash_algos() 
     * @param {mixed} $data 需要加密的字符串或文件
     * @param {bool} $isBase64 是否base64编码
     * @return {*}
     */
    public function hash(string $algo, mixed $data, bool $isBase64 = true):string
    {
        $func   = $this->_isFile($data) ? 'hash_file' : 'hash';
        // 第三个参数为false时输出小写16进制字符串,否出输出原始二进制数据
        $result = $func($algo, $data, true);
        if ($result === false) {
            throw new \Exception('加密失败');
        }
        return $isBase64 ? base64_encode($result) : trim($result);
    }

    /**
     * @description: hash HMAC签名
     * @param {string} $algo 签名算法名称,支持算法:hash_hmac_algos()
     * @param {mixed} $data 需要加密的字符串或文件
     * @param {string} $key 签名需要的key
     * @param {bool} $isBase64 是否base64编码
     * @return {*}
     */
    public function hashSign(string $algo, mixed $data, string $key, bool $isBase64 = true):string
    {
        $func   = $this->_isFile($data) ? 'hash_hmac_file' : 'hash_hmac';
        // 第四个参数为false时输出小写16进制字符串,否出输出原始二进制数据
        $result = $func($algo, $data, $key, true);
        if ($result === false) {
            throw new \Exception('加密失败');
        }
        return $isBase64 ? base64_encode($result) : $result;
    }

    /**
     * @description: hash PBKDF2签名,
     *             PBKDF2原理:通过一个伪随机函数(例如HMAC函数),把明文和一个盐值作为输入参数,然后重复进行运算,并最终产生密钥
     * @param {string} $algo 签名算法名称,支持算法:hash_algos(),不支持非加密哈希函数((adler32,crc32,crc32b,fnv132,fnv1a32,fnv164,fnv1a64,joaat))
     * @param {string} $data 需要加密的字符串
     * @param {string} $key 签名需要的key
     * @param {int} $iterations 签名的迭代次数
     * @param {int} $length 签名结果的长度,为0时,结果长度为算法固定长度
     * @param {bool} $isBase64 是否base64编码
     * @return {*}
     */
    public function hashPbkdf2(string $algo, string $data, string $key, int $iterations, int $length = 0, bool $isBase64 = true):string
    {
        // 第六个参数为false时输出小写16进制字符串,否出输出原始二进制数据
        $result = hash_pbkdf2($algo, $data, $key, $iterations, $length, true);
        return $isBase64 ? base64_encode($result) : $result;
    }

    /**
     * @description: hash HKDF签名
     *             HKDF原理:使用原始的密钥材料,派生出一个或更多个能达到密码学强度的密钥(主要是保证随机性)——就是将较短的密钥材料扩展成较长的密钥材料,
     *                       过程中需要保证随机性。
     *             HKDF包含两个基本模块:
     *                                  1、提取:使用原始的密钥材料,派生出一个符合密码学强度的伪随机密钥
     *                                  2、扩展:使用第1步骤提取出来的伪随机密钥,扩展出指定长度的密钥(同时保证随机性)
     * @param {string} $algo 签名算法名称,支持算法:hash_algos(),不允许使用非加密哈希函数
     * @param {string} $data 需要加密的字符串
     * @param {string} $key 签名需要的key,提高HKDF的强度
     * @param {string} $info 应用程序/上下文特定的信息字符串
     * @param {int} $length 签名结果的长度,为0时,结果长度为算法固定长度
     * @return {*}
     */
    public function hashHkdf(string $algo, string $data, string $key = '', string $info = '', int $length = 0):string
    {
        return hash_hkdf($algo, $data, $length, $info, $key);
    }

    /**
     * @description: hash字符串对比,可防止时序攻击,对比的两个字符串长度必须相同,否则直接返回false
     * @param {string} $hash1 已知hash字符串
     * @param {string} $hash2 需要比较的hash字符串
     * @return {*}
     */
    public function hashContrast(string $hash1, string $hash2):bool
    {
        return hash_equals($hash1, $hash2);
    }
}

 

{/if}