亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

PHP實(shí)現(xiàn)sha-256哈希算法實(shí)例代碼

瀏覽:231日期:2022-06-06 18:33:13
目錄
  • 前言
  • 準(zhǔn)備一:代碼主體
  • 準(zhǔn)備二:助手函數(shù)
    • 步驟一:字符串轉(zhuǎn)二進(jìn)制
    • 步驟二:追加數(shù)字 1
    • 步驟三:填充至 512 的倍數(shù)
    • 步驟四:追加原始長(zhǎng)度信息
    • 步驟五:切分區(qū)塊并填充至 2048 位
    • 步驟六:區(qū)塊數(shù)據(jù)修改
    • 步驟七:壓縮
  • 總結(jié)

    前言

    哈希 又稱(chēng)作 “散列”,它接收任何一組任意長(zhǎng)度的輸入信息,通過(guò) 哈希 算法變換成固定長(zhǎng)度的數(shù)據(jù)指紋,該指紋就是 哈希值。總體而言,哈希 可理解為一種消息摘要。

    在 PHP 中有這個(gè)函數(shù) hash(),可以計(jì)算字符串的哈希值,出于好奇我 Google 了一下哈希計(jì)算的具體步驟,并使用 PHP 編寫(xiě)了一套計(jì)算 sha-256 哈希值的代碼。當(dāng)然除了 sha-256 以外還有一些別的哈希算法,只是目前 sha-256 用的多一些。下面是目前 美國(guó)國(guó)家標(biāo)準(zhǔn)與技術(shù)研究院 發(fā)布哈希算法:

    哈希算法輸入大小(bits)分塊大小(bits)行大小(bits)生成二進(jìn)制長(zhǎng)度(bits)生成十六進(jìn)制長(zhǎng)度(chars)sha1< 2^645123216040sha-224< 2^645123222456sha-256< 2^645123225664sha-384< 2^12810246438496sha-512< 2^128102464512128sha-512/224< 2^12810246422456sha-512/256< 2^12810246425664

    在編寫(xiě)過(guò)程中我主要參考了以下文檔和站點(diǎn):

    Lane Wagner - How SHA-256 Works Step-By-Step:https://blog.boot.dev/cryptography/how-sha-2-works-step-by-step-sha-256/

    Secure Hash Standard (SHS) - FIPS 180-4(官方文檔):https://csrc.nist.gov/publications/detail/fips/180/4/final

    ASCII Table:https://www.asciitable.com/

    本文內(nèi)容較多,主要分為下面這幾個(gè)部分,讀者閱讀時(shí)可以先跳過(guò) 準(zhǔn)備二:助手方法 直接進(jìn)入 步驟 部分,在閱讀 步驟 部分需要用到指定方法時(shí)再回過(guò)頭來(lái)查閱 準(zhǔn)備二:助手方法 中的函數(shù)。

    準(zhǔn)備一:代碼主體

    準(zhǔn)備二:助手方法(閱讀時(shí)可先跳過(guò))

    步驟一:字符串轉(zhuǎn)二進(jìn)制

    步驟二:追加數(shù)字 1

    步驟三:填充至 512 的倍數(shù)

    步驟四:追加原始長(zhǎng)度信息

    步驟五:切分區(qū)塊并填充至 2048 位

    步驟六:區(qū)塊數(shù)據(jù)修改

    步驟七:壓縮

    準(zhǔn)備一:代碼主體

    我們創(chuàng)建一個(gè)類(lèi) Algorithm 來(lái)存放我們計(jì)算哈希所需要用到的方法和屬性。這個(gè)類(lèi)中只有一個(gè) public 的方法 sha256(),此方法傳入一個(gè)字符串參數(shù),輸出此字符串的 sha-256 哈希值。要完成我們的哈希計(jì)算,總共需要經(jīng)過(guò)七個(gè)步驟,我們先把這七個(gè)步驟的調(diào)用寫(xiě)到 sha256() 的函數(shù)體中。

    <?php 
    declare(strict_types=1);
    class Algorithm
    {
        public function sha256(string $str): string
        {
    // 步驟一:將字符串轉(zhuǎn)化為二進(jìn)制
     $this->step1_convert_str_to_bits($str);
     
    // 步驟二:在最后面追加一個(gè)1
     $this->step2_append_1();
     
    // 步驟三:在數(shù)據(jù)末尾添加0,確保二進(jìn)制的個(gè)數(shù)是512的倍數(shù),最后預(yù)留64位用于存儲(chǔ)原始長(zhǎng)度信息
     $this->step3_extend_to_multiple_of_512();
     
    // 步驟四:把原始字符串位長(zhǎng)度,填充到預(yù)留在最后的64位(8個(gè)字節(jié)的長(zhǎng)整型)中
     $this->step4_append_origin_length();
     
    // 步驟五:每一個(gè)512位切分區(qū)塊,在區(qū)塊末尾填充0,使得每個(gè)區(qū)塊位數(shù)為2048位,需要增加48行(32位一行)
     $this->step5_split_blocks_and_append_48_lines();
     
    // 步驟六:針對(duì)每一個(gè)2048位區(qū)塊處理:以32位為一行,總共有64行,修改【16-63】行的數(shù)據(jù)
     $this->step6_modify_blocks_appended_48_lines();
     
    // 步驟七:壓縮數(shù)據(jù),生成最終的哈希值
    return $this->step7_compress_to_final_hash();
        }
    }

    除了 sha256() 這個(gè)函數(shù)外, 我們要需要幾個(gè)成員屬性來(lái)保存計(jì)算過(guò)程中產(chǎn)生的數(shù)據(jù)。

    $originLen 屬性用于記錄字符串被轉(zhuǎn)化為二進(jìn)制之后的原始長(zhǎng)度,這個(gè)長(zhǎng)度值后續(xù)會(huì)追加到數(shù)據(jù)中去。

    /** @var int 原始數(shù)據(jù)的二進(jìn)制長(zhǎng)度  */
    private int $originLen = 0;

    $bits 屬性用于儲(chǔ)存字符串轉(zhuǎn)化后得到的二進(jìn)制數(shù)據(jù)。

    /** @var array 存儲(chǔ)二進(jìn)制數(shù)組 */
    private array $bits;

    $blocks 存放分塊后的二進(jìn)制數(shù)據(jù)。

    /** @var array 二進(jìn)制區(qū)塊 */
    private array $blocks;

    H 哈希計(jì)所需的常量,hash-256 的 8 個(gè)哈希常量是質(zhì)數(shù) 2、3、5、7、11、13、17、19 各自平方根取二進(jìn)制小數(shù)部分前 32 位所得。

    /** @var array 質(zhì)數(shù)平方根常量 */
    private const H = [
        0x6a09e667, // 質(zhì)數(shù)2的平方根取二進(jìn)制小數(shù)部分前32位
        0xbb67ae85, // 質(zhì)數(shù)3的平方根取二進(jìn)制小數(shù)部分前32位
        0x3c6ef372, // 質(zhì)數(shù)5的平方根取二進(jìn)制小數(shù)部分前32位
        0xa54ff53a, // 質(zhì)數(shù)7的平方根取二進(jìn)制小數(shù)部分前32位
        0x510e527f, // 質(zhì)數(shù)11的平方根取二進(jìn)制小數(shù)部分前32位
        0x9b05688c, // 質(zhì)數(shù)13的平方根取二進(jìn)制小數(shù)部分前32位
        0x1f83d9ab, // 質(zhì)數(shù)17的平方根取二進(jìn)制小數(shù)部分前32位
        0x5be0cd19, // 質(zhì)數(shù)19的平方根取二進(jìn)制小數(shù)部分前32位
    ];

    對(duì)于上面這幾個(gè)常量,感興趣的同學(xué)也可以自己計(jì)算得到,我這里只提供一個(gè)簡(jiǎn)單的計(jì)算示例,以質(zhì)數(shù) 2 為例,我們先通過(guò)計(jì)算器得到它的平方根:1.4142135623730950488016887242097 然后只取小數(shù)部分:0.4142135623730950488016887242097,接著將這個(gè)十進(jìn)制的小數(shù)轉(zhuǎn)為二進(jìn)制,轉(zhuǎn)為流程如下:

    小數(shù)轉(zhuǎn)二進(jìn)制
        0.
    0.4142135623730950488016887242097 x 2 => 0
    0.8284271247461900976033774484194 x 2 => 1
    0.6568542494923801952067548968388 x 2 => 1
    0.3137084989847603904135097936776 x 2 => 0
    0.6274169979695207808270195873552 x 2 => 1
    0.2548339959390415616540391747104 x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 1
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 1
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 1
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 1
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 1
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 1
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    0.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ x 2 => 0
    . . .

    上面計(jì)算得到的小數(shù)部分二進(jìn)制,取前 32 位:01101010 00001001 11100110 01100111,轉(zhuǎn)為十六進(jìn)制表示:0x6a09e667,其他幾個(gè)質(zhì)數(shù)的計(jì)算也是類(lèi)似。當(dāng)然由于是常量,值是固定不變的,所以我們只要知道其計(jì)算原理即可。

    和上面的平方根常量類(lèi)似,hash-256 的另外 64 個(gè)常量是質(zhì)數(shù) 2、3、5、…、311 各自立方根取二進(jìn)制小數(shù)部分前 32 位。

    /** @var array 質(zhì)數(shù)立方根常量 */
    private const K = [
        0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
         0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
         0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
         0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
         0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
         0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
         0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
         0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
    ];

    準(zhǔn)備二:助手函數(shù)

    你可以直接跳過(guò)此部分內(nèi)容,從下面的 步驟一 開(kāi)始著手去計(jì)算哈希值,當(dāng)需要使用到某一個(gè)助手函數(shù)的時(shí)候再來(lái)這里查找即可。

    在計(jì)算哈希的過(guò)程中,我們是把二進(jìn)制數(shù)據(jù)存儲(chǔ)到數(shù)組中的,數(shù)組中的每一個(gè)元素對(duì)應(yīng)了二進(jìn)制的一個(gè)比特位,所以如果要對(duì)這些二進(jìn)制數(shù)組進(jìn)行 與 非 異或 相加 等操作,我們就需要實(shí)現(xiàn)自己的操作函數(shù)。

    十進(jìn)制整數(shù)轉(zhuǎn)化為二進(jìn)制數(shù)組。

    /**
     * 十進(jìn)制整數(shù)轉(zhuǎn)化為二進(jìn)制數(shù)組
     * @param int $num 十進(jìn)制整數(shù)
     * @param int $fillTo 填充到多少位,不夠的用0來(lái)補(bǔ)齊
     */
     
    public function int2bits(int $num, int $fillTo = 0): array
    {
        $bits = str_split(decbin($num));
        array_walk($bits, function (&$val) {
    $val = intval($val);
        });
     
        for ($len = count($bits); $len < $fillTo; $len++) {
    array_unshift($bits, 0);
        }
        return $bits;
    }

    二進(jìn)制數(shù)組向右移動(dòng)指定位數(shù)。

    /**
     * 二進(jìn)制數(shù)組向右移動(dòng)
     * @param array $bits 二進(jìn)制數(shù)組
     */
     
    public function rightShift(array $bits, int $move): array
     
    {
        $len = count($bits);
        $move = $move % $len;
        if ($move <= 0) return $bits;
        return array_merge(array_fill(0, $move, 0), array_slice($bits, 0, $len-$move));
    }

    二進(jìn)制數(shù)組向右旋轉(zhuǎn),與右移類(lèi)似,不過(guò)移出去的數(shù)要插回到頭部。

    /**
     * 二進(jìn)制數(shù)組向右旋轉(zhuǎn)
     * @param array $bits 二進(jìn)制數(shù)組
     */
     
    public function rightRotate(array $bits, int $move): array
    {
        $len = count($bits);
        $move = $move % $len;
        if ($move <= 0) return $bits;
        return array_merge(array_slice($bits, $len-$move, $move), array_slice($bits, 0, $len-$move));
    }

    二進(jìn)制數(shù)組求 非。

    /**
     * 二進(jìn)制數(shù)組求非
     * @param array $bits 二進(jìn)制數(shù)組
     */
     
    public function not(array $bits): array
    {
        for ($i = count($bits)-1; $i >= 0; $i--) {
    $bits[$i] = ($bits[$i] == 0) ? 1 : 0;
        }
        return $bits;
    }

    多個(gè)二進(jìn)制數(shù)組相 與。

    /**
     * 二進(jìn)制數(shù)組求與
     * @param array $args 二進(jìn)制數(shù)組
     */
     
    public function and(array ...$args): array
    {
     
        $argc = count($args);
        if ($argc == 0) return [];
        for ($i = 1; $i < $argc; $i++) {
    $j = count($args[0]) - 1;
    $k = count($args[$i]) - 1;
    while ($j >= 0 || $k >= 0) {
        $j < 0 and array_unshift($args[0], 0) and $j = 0; // 如果是$args[0]不夠長(zhǎng)就頭插補(bǔ)齊
        ($args[$i][$k] ?? 0) == 0 and $args[0][$j] = 0;
        $j--;
        $k--;
    }
        }
        return $args[0];
    }

    多個(gè)二進(jìn)制數(shù)組求 異或。

    /**
     * 二進(jìn)制數(shù)組求異或
     * @param array $args 二進(jìn)制數(shù)組
     */
     
    public function xor(array ...$args): array
    {
        $argc = count($args);
        if ($argc == 0) return [];
        for ($i = 1; $i < $argc; $i++) {
    $j = count($args[0]) - 1;
    $k = count($args[$i]) - 1;
    while ($j >= 0 || $k >= 0) {
        $j < 0 and array_unshift($args[0], 0) and $j = 0; // 如果是$args[0]不夠長(zhǎng)就頭插補(bǔ)齊
        $args[0][$j] = intval($args[0][$j] != ($args[$i][$k] ?? 0));
        $j--;
        $k--;
    }
        }
        return $args[0];
    }

    多個(gè)二進(jìn)制數(shù)組 相加。

    /**
     * 二進(jìn)制數(shù)組相加
     * @param array $args 二進(jìn)制數(shù)組
     */
     
    public function add(array ...$args): array
    {
     
        $argc = count($args);
        if ($argc == 0) return [];
        for ($i = 1; $i < $argc; $i++) {
    $carry = 0;
    $j = count($args[0]) - 1;
    $k = count($args[$i]) - 1;
    while ($j >= 0 || $k >= 0) {
        $j < 0 and array_unshift($args[0], 0) and $j = 0; // 如果是$args[0]不夠長(zhǎng)就頭插補(bǔ)齊
        $carry += $args[0][$j] + ($args[$i][$k] ?? 0);
        switch ($carry) {
     case 1: $carry = 0; $args[0][$j] = 1; break;
    case 2: $carry = 1; $args[0][$j] = 0; break;
    case 3: $carry = 1; $args[0][$j] = 1; break;
        }
        $j--;
        $k--;
    }
    $carry == 1 and array_unshift($args[0], $carry); // 計(jì)算完后還有進(jìn)位則加長(zhǎng)存放
         }
        return array_slice($args[0], -32); // 計(jì)算結(jié)果只保留32位
    }

    打印二進(jìn)制數(shù)組,用于調(diào)試用途,每 8 位會(huì)補(bǔ)一個(gè)空格,每 32 位補(bǔ)兩個(gè)空格,每 64 位換一行,每 512 位空一行,讓打印的數(shù)據(jù)更容易查看。

    /**
     * 打印二進(jìn)制數(shù)組
     * @param array $bits 二進(jìn)制數(shù)組
     */
     
    public function printBits(array $bits): void
    {
        $len = 0;
        foreach ($bits as $bit) {
    if ($len > 0) {
        if ($len % 512 == 0) echo PHP_EOL;
        if ($len % 64 == 0) {
    echo PHP_EOL;  
        } else {
    if ($len % 32 == 0) echo " ";
    if ($len % 8 == 0) echo " ";
        }
    }
    echo $bit;
    $len++;
        }
        echo PHP_EOL;
    }

    二進(jìn)制數(shù)組轉(zhuǎn)化為十六進(jìn)制,用于最后一步將二進(jìn)制轉(zhuǎn)換為哈希值字符串。

    /**
     * 二進(jìn)制數(shù)組轉(zhuǎn)化為十六進(jìn)制
     * @param array $bits 二進(jìn)制數(shù)組
     */
     
    public function bits2hex(array $bits): string
    {
        $str = "";
        for ($i = count($bits)-1; $i >= 0; $i -= 4) {
    $dec = $bits[$i] + ($bits[$i-1] ?? 0)*2 + ($bits[$i-2] ?? 0)*4 + ($bits[$i-3] ?? 0)*8;
    switch ($dec) {
        case 0:  $str = "0" . $str; break;
        case 1:  $str = "1" . $str; break;
        case 2:  $str = "2" . $str; break;
        case 3:  $str = "3" . $str; break;
        case 4:  $str = "4" . $str; break;
        case 5:  $str = "5" . $str; break;
        case 6:  $str = "6" . $str; break;
        case 7:  $str = "7" . $str; break;
        case 8:  $str = "8" . $str; break;
        case 9:  $str = "9" . $str; break;
        case 10: $str = "a" . $str; break;
        case 11: $str = "b" . $str; break;
        case 12: $str = "c" . $str; break;
        case 13: $str = "d" . $str; break;
        case 14: $str = "e" . $str; break;
        case 15: $str = "f" . $str; break;
    }
        }
        return $str;
    }

    步驟一:字符串轉(zhuǎn)二進(jìn)制

    這里我們使用 "hello world" 字符串來(lái)演示整個(gè)哈希計(jì)算過(guò)程。我們可以先用 PHP 內(nèi)置的哈希函數(shù)將結(jié)果算出來(lái), "hello world" 的哈希值是 "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",到最后我們計(jì)算出來(lái)的哈希值如果等于這個(gè)值則說(shuō)明我們的計(jì)算邏輯是正確的。

    首先我們把 "hello world" 拆成一個(gè)個(gè)的字符,每個(gè)字符都有對(duì)應(yīng)一個(gè) ASCII 碼值,這些 ASCII 碼值都是 0-256 的整數(shù)。使用 PHP 的 ord() 函數(shù)可以把這些字符轉(zhuǎn)為整數(shù),再將這些整數(shù)轉(zhuǎn)為對(duì)應(yīng)的二進(jìn)制并存儲(chǔ)到屬性 $bits 中。并將此時(shí) $bits 的長(zhǎng)度值保存到 $originLen 屬性里。

    "hello world" 轉(zhuǎn)為二進(jìn)制后的數(shù)據(jù)是:

    “hello world”

    01101000 01100101 01101100 01101100  01101111 00100000 01110111 01101111

    01110010 01101100 01100100

    /**
     * 步驟一:將字符串轉(zhuǎn)化為二進(jìn)制
     * @param string $str 原始字符串
     */
     
    public function step1_convert_str_to_bits(string $str): void
    {
     
        $this->bits = [];
        $chars = str_split($str);
        foreach ($chars as $char) {
    $this->bits = array_merge($this->bits, $this->int2bits(ord($char), 8));
        }
        $this->originLen = count($this->bits);
    }

    步驟二:追加數(shù)字 1

    接著在二進(jìn)制數(shù)組的末尾添加一個(gè) 1。

    $bits

    01101000 01100101 01101100 01101100  01101111 00100000 01110111 01101111

    01110010 01101100 01100100 1

    /**
     * 步驟二:在最后面追加一個(gè)1
     */
     
    public function step2_append_1(): void
    {
        $this->bits[] = 1;
    }

    步驟三:填充至 512 的倍數(shù)

    在二進(jìn)制數(shù)組的末尾添加 0 以使得整個(gè)二進(jìn)制數(shù)組的個(gè)數(shù)剛好是 512 的倍數(shù)。需要注意的是,二進(jìn)制數(shù)組的最末尾要預(yù)留 64 位用于存放原始二進(jìn)制的長(zhǎng)度。也就是一開(kāi)始將字符串轉(zhuǎn)換成二進(jìn)制時(shí)的長(zhǎng)度,我們?cè)?步驟一 中將這個(gè)長(zhǎng)度值保存到了 $originLen 屬性里。

    $bits

    01101000 01100101 01101100 01101100  01101111 00100000 01110111 01101111

    01110010 01101100 01100100 10000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    [    預(yù)留 64 位用于存儲(chǔ)原始字符串的長(zhǎng)度    ]

    /**
     * 步驟三:在數(shù)據(jù)末尾添加0,確保二進(jìn)制的個(gè)數(shù)是512的倍數(shù),最后預(yù)留64位用于存儲(chǔ)原始長(zhǎng)度信息
     */
     
    public function step3_extend_to_multiple_of_512(): void
    {
        $rem = (count($this->bits) + 64) % 512;
        if ($rem > 0) {
    while ($rem < 512) {
        $this->bits[] = 0;
        $rem++;
    }
        }
    }

    步驟四:追加原始長(zhǎng)度信息

    把之前記錄的原始數(shù)據(jù)長(zhǎng)度 $originLen 轉(zhuǎn)換為 64 位的二進(jìn)制追加到 $bits 末尾。

    $bits

    01101000 01100101 01101100 01101100  01101111 00100000 01110111 01101111

    01110010 01101100 01100100 10000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000  00000000 00000000 00000000 01011000

    /**
     * 步驟四:把原始字符串位長(zhǎng)度,填充到預(yù)留在最后的64位(8個(gè)字節(jié)的長(zhǎng)整型)中
     */
     
    public function step4_append_origin_length(): voi
     
    {
        $this->bits = array_merge($this->bits, $this->int2bits($this->originLen, 64));
    }

    步驟五:切分區(qū)塊并填充至 2048 位

    經(jīng)過(guò) 步驟四 之后,$bits 二進(jìn)制數(shù)組的個(gè)數(shù)已經(jīng)是 512 的倍數(shù),現(xiàn)在以每 512 位分為一個(gè)區(qū)塊,然后在每個(gè)區(qū)塊末尾填充 0,讓每個(gè)區(qū)塊的大小變成 2048 位。每個(gè)區(qū)塊的 2048 位數(shù)據(jù)以 32 位作為一行,那么就有 64 行。由于 "hello world" 數(shù)據(jù)比較短,我們就只有一個(gè)區(qū)塊。

    -$blocks[0]$blocks[0]-0
    2
    4
    6
    8
    10
    12
    14

    16
    18
    20
    22
    24
    26
    28
    30

    32
    34
    36
    38
    40
    42
    44
    46

    48
    50
    52
    54
    56
    58
    60
    6201101000 01100101 01101100 01101100
    01110010 01101100 01100100 10000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 0000000001101111 00100000 01110111 01101111
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 01011000

    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000
    00000000 00000000 00000000 000000001
    3
    5
    7
    9
    11
    13
    15

    17
    19
    21
    23
    25
    27
    29
    31

    33
    35
    37
    39
    41
    43
    45
    47

    49
    51
    53
    55
    57
    59
    61
    63
    /**
     * 步驟五:每一個(gè)512位切分區(qū)塊,在區(qū)塊末尾填充0,使得每個(gè)區(qū)塊位數(shù)為2048位,經(jīng)計(jì)算
     * 每個(gè)區(qū)塊還需要添加48x32個(gè)0
     */
     
    public function step5_split_blocks_and_append_48_lines(): void
    {
        $this->blocks = [];
        $append = $this->int2bits(0, 48 * 32);
        $len = count($this->bits);
        for ($i = 0; $i < $len; $i += 512) {
    $this->blocks[] = array_merge(array_slice($this->bits, $i, 512), $append);
        }
    }

    步驟六:區(qū)塊數(shù)據(jù)修改

    上一步中我們給每一個(gè)區(qū)塊末尾添加了很多 0,在這一步中,通過(guò)一些位操作將這些數(shù)據(jù)進(jìn)一步調(diào)整。按 32 位為一行,我們需要修改新增加的 16-63 行的數(shù)據(jù)。修改的邏輯如下:

    算法邏輯

    For i from w[16…63]:
    ????s0 = (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3)
    ????s1 = (w[i-2] rightrotate 17) xor (w[i- 2] rightrotate 19) xor (w[i- 2] rightshift 10)
     ????w[i] = w[i-16] + s0 + w[i-7] + s1

    其中 w 是每個(gè)區(qū)塊的行數(shù)組,w[i] 就是第 i 行。

    rightshift 是右移,rightrotate 是旋轉(zhuǎn)右移, xor 是異或。

    這里以第 16 行的處理為例:

    算法詳解

    i = 16
    (w[1] rightrotate 7) = 01101111001000000111011101101111 -> 11011110110111100100000011101110
    (w[1] rightrotate 18) = 01101111001000000111011101101111 -> 00011101110110111101101111001000
    (w[1] rightshift 3) = 01101111001000000111011101101111 -> 00001101111001000000111011101101
    s0 = (w[1] rightrotate 7) xor (w[1] rightrotate 18) xor (w[1] rightshift 3)
    ?= 11001110111000011001010111001011
    (w[14] rightrotate 17) = 00000000000000000000000000000000 -> 00000000000000000000000000000000
    (w[14] rightrotate 19) = 00000000000000000000000000000000 -> 00000000000000000000000000000000
    (w[14] rightshift 10) = 00000000000000000000000000000000 -> 00000000000000000000000000000000
    s1 = (w[14] rightrotate 17) xor (w[14] rightrotate 19) xor (w[14] rightshift 10)
    = 00000000000000000000000000000000
    w[i] = w[0] + s0 + w[9] + s1
    = 00110111010001110000001000110111(相加得到的值如果超過(guò) 32 位,則抹去高位)
    /**
     * 步驟六:針對(duì)每一個(gè)2048位區(qū)塊處理:以32位為一行,總共有64行,修改【16-63】行的數(shù)據(jù),
     * 這【16-63】行就是上一步新增的48x32個(gè)0
     */
     
    public function step6_modify_blocks_appended_48_lines(): void
    {
        foreach ($this->blocks as &$block) {
    for ($i = 16; $i < 64; $i++) {
        $w0 = array_slice($block, ($i-16)*32, 32);
        $w1 = array_slice($block, ($i-15)*32, 32);
        $w9 = array_slice($block, ($i-7)*32, 32);
        $w14 = array_slice($block, ($i-2)*32, 32);
        $s0 = $this->xor(   
    $this->rightRotate($w1, 7),
    $this->rightRotate($w1, 18),
    $this->rightShift($w1, 3)
        );
     
        $s1 = $this->xor(
    $this->rightRotate($w14, 17),
    $this->rightRotate($w14, 19),
    $this->rightShift($w14, 10)
        );
     
        $wi = $this->add($w0, $s0, $w9, $s1);
        // 如果$wi的長(zhǎng)度超過(guò)了32位,則只取32位,舍棄高位
        $k = count($wi) - 1;
        for ($j = $i * 32 + 31; $j >= $i * 32; $j--) {
    $block[$j] = $wi[$k] ?? 0;
    $k--;
        }
    }
        }
    }

    步驟七:壓縮

    新建變量 $a、$b、$c、$d、$e、$f、$g、$h 值依次分別等于哈希常量 H[0-7],接著循環(huán)每一個(gè)區(qū)塊的每一行,通過(guò) 與 非 異或 等操作將信息壓縮到 $a、$b、$c、$d、$e、$f、$g、$h 中,最后將 $a、$b、$c、$d、$e、$f、$g、$h 的值與原始常量 H[0-7] 相加,拼接相加后的二進(jìn)制結(jié)果 h0~h7 并轉(zhuǎn)化為十六進(jìn)制字符串得到最終的哈希值。

    具體的壓縮算法如下:

    算法邏輯

    For i from 0 to 63
    ????s1 = (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
    ????ch = (e and f) xor ((not e) and g)
    ????temp1 = h + s1 + ch + k[i] + w[i]
    ????s0 = (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
    ????maj = (a and b) xor (a and c) xor (b and c)
    ????temp2 := s0 + maj
    ????h = g
    ????g = f
    ????f = e
    ????e = d + temp1
    ????d = c
    ????c = b
    ????b = a
    ????a = temp1 + temp2

    這里以第 0 行的處理為例,列出了變量計(jì)算結(jié)果方便大家對(duì)照調(diào)試:

    計(jì)算結(jié)果

    i = 0

    s1 = 00110101100001110010011100101011

    ch = 00011111100001011100100110001100

    temp1 = 01011011110111010101100111010100

    s0 = 11001110001000001011010001111110

    maj = 00111010011011111110011001100111

    temp2 = 00001000100100001001101011100101

    h = 00011111100000111101100110101011

    g = 10011011000001010110100010001100

    f = 01010001000011100101001001111111

    e = 00000001001011010100111100001110

    d = 00111100011011101111001101110010

    c = 10111011011001111010111010000101

    b = 01101010000010011110011001100111

    a = 01100100011011011111010010111001

    /**
     * 步驟七:壓縮數(shù)據(jù)
     */
     
    public function step7_compress_to_final_hash(): string
    {
     
        $a = $h0 = $this->int2bits(static::H[0], 32);
        $b = $h1 = $this->int2bits(static::H[1], 32);
        $c = $h2 = $this->int2bits(static::H[2], 32);
        $d = $h3 = $this->int2bits(static::H[3], 32);
        $e = $h4 = $this->int2bits(static::H[4], 32);
        $f = $h5 = $this->int2bits(static::H[5], 32);
        $g = $h6 = $this->int2bits(static::H[6], 32);
        $h = $h7 = $this->int2bits(static::H[7], 32);
        foreach ($this->blocks as $block) {
    for ($i = 0; $i < 64; $i++) {
        $s1 = $this->xor(
    $this->rightRotate($e, 6),
    $this->rightRotate($e, 11),
    $this->rightRotate($e, 25)
        );
     
        $ch = $this->xor(
    $this->and($e, $f),
    $this->and($this->not($e), $g)
        );
     
        $ki = $this->int2bits(static::K[$i], 32);
        $wi = array_slice($block, $i*32, 32);
        $temp1 = $this->add($h, $s1, $ch, $ki, $wi);
        $s0 = $this->xor(
    $this->rightRotate($a, 2),
    $this->rightRotate($a, 13),
    $this->rightRotate($a, 22),
        );
     
        $maj = $this->xor(
    $this->and($a, $b),
    $this->and($a, $c),
    $this->and($b, $c)
        );
     
        $temp2 = $this->add($s0, $maj);
        $h = $g;
        $g = $f;
        $f = $e;
        $e = $this->add($d, $temp1);
        $d = $c;
        $c = $b;
        $b = $a;
        $a = $this->add($temp1, $temp2);
    }
        }
     
        $h0 = $this->add($h0, $a);
        $h1 = $this->add($h1, $b);
        $h2 = $this->add($h2, $c);
        $h3 = $this->add($h3, $d);
        $h4 = $this->add($h4, $e);
        $h5 = $this->add($h5, $f);
        $h6 = $this->add($h6, $g);
        $h7 = $this->add($h7, $h);
        return $this->bits2hex(array_merge($h0, $h1, $h2, $h3, $h4, $h5, $h6, $h7));
    }

    至此整個(gè)哈希 sha-256 計(jì)算流程就完成了, 計(jì)算得到的哈希值也與 PHP 自帶的 hash() 函數(shù)計(jì)算結(jié)果一致。

    總結(jié)

    到此這篇關(guān)于PHP實(shí)現(xiàn)sha-256哈希算法的文章就介紹到這了,更多相關(guān)PHP實(shí)現(xiàn)sha-256哈希算法內(nèi)容請(qǐng)搜索以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持!

    標(biāo)簽: PHP
    相關(guān)文章:
    主站蜘蛛池模板: 国产片一区二区三区 | 成人国产激情福利久久精品 | 狼人 成人 综合 亚洲 | 欧美亚洲精品一区 | 在线成人免费视频 | 免费国产高清精品一区在线 | 欧美精品二区 | 久久精品亚洲综合 | 亚洲精品在线观看91 | 国产三级精品三级在线观看 | 国产高清第一页 | 1000部未满岁18在线观看网站 | 亚洲成年看片在线观看男男 | 国产高清免费午夜在线视频 | 香蕉在线视频观看 | 中文字幕久久久久一区 | 在线a人片免费观看国产 | 高清视频 一区二区三区四区 | 亚洲国产第一区二区香蕉日日 | 一级黄视频 | 一级毛片免费毛片一级毛片免费 | 国产精品v免费视频 | a毛片在线看片免费 | 2019理论国产一级中字 | 精品欧美高清一区二区免费 | 欧美大尺度aaa级毛片 | 日本 亚洲 欧美 | 爽爽影院色黄网站在线观看 | 国产成人免费在线视频 | 中国特级黄色毛片 | 午夜视频偷拍在线观看免费 | 亚洲一区二区成人 | 国产日韩欧美亚洲精品95 | 精品欧美亚洲韩国日本久久 | 国产视频二区在线观看 | 香蕉视频久久 | 欧美日韩国产精品综合 | 免费特级黄毛片在线成人观看 | a高清免费毛片久久 | 国产精品免费久久久免费 | 一级骚片超级骚在线观看 |