电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

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

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

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

    前言

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

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

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

    在編寫過程中我主要參考了以下文檔和站點:

    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)容較多,主要分為下面這幾個部分,讀者閱讀時可以先跳過 準(zhǔn)備二:助手方法 直接進入 步驟 部分,在閱讀 步驟 部分需要用到指定方法時再回過頭來查閱 準(zhǔn)備二:助手方法 中的函數(shù)。

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

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

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

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

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

    步驟四:追加原始長度信息

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

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

    步驟七:壓縮

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

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

    <?php 
    declare(strict_types=1);
    class Algorithm
    {
        public function sha256(string $str): string
        {
    // 步驟一:將字符串轉(zhuǎn)化為二進制
     $this->step1_convert_str_to_bits($str);
     
    // 步驟二:在最后面追加一個1
     $this->step2_append_1();
     
    // 步驟三:在數(shù)據(jù)末尾添加0,確保二進制的個數(shù)是512的倍數(shù),最后預(yù)留64位用于存儲原始長度信息
     $this->step3_extend_to_multiple_of_512();
     
    // 步驟四:把原始字符串位長度,填充到預(yù)留在最后的64位(8個字節(jié)的長整型)中
     $this->step4_append_origin_length();
     
    // 步驟五:每一個512位切分區(qū)塊,在區(qū)塊末尾填充0,使得每個區(qū)塊位數(shù)為2048位,需要增加48行(32位一行)
     $this->step5_split_blocks_and_append_48_lines();
     
    // 步驟六:針對每一個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() 這個函數(shù)外, 我們要需要幾個成員屬性來保存計算過程中產(chǎn)生的數(shù)據(jù)。

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

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

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

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

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

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

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

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

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

    小數(shù)轉(zhuǎ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
    . . .

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

    和上面的平方根常量類似,hash-256 的另外 64 個常量是質(zhì)數(shù) 2、3、5、…、311 各自立方根取二進制小數(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ù)

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

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

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

    /**
     * 十進制整數(shù)轉(zhuǎn)化為二進制數(shù)組
     * @param int $num 十進制整數(shù)
     * @param int $fillTo 填充到多少位,不夠的用0來補齊
     */
     
    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;
    }

    二進制數(shù)組向右移動指定位數(shù)。

    /**
     * 二進制數(shù)組向右移動
     * @param array $bits 二進制數(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));
    }

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

    /**
     * 二進制數(shù)組向右旋轉(zhuǎn)
     * @param array $bits 二進制數(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));
    }

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

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

    多個二進制數(shù)組相 與。

    /**
     * 二進制數(shù)組求與
     * @param array $args 二進制數(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]不夠長就頭插補齊
        ($args[$i][$k] ?? 0) == 0 and $args[0][$j] = 0;
        $j--;
        $k--;
    }
        }
        return $args[0];
    }

    多個二進制數(shù)組求 異或。

    /**
     * 二進制數(shù)組求異或
     * @param array $args 二進制數(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]不夠長就頭插補齊
        $args[0][$j] = intval($args[0][$j] != ($args[$i][$k] ?? 0));
        $j--;
        $k--;
    }
        }
        return $args[0];
    }

    多個二進制數(shù)組 相加。

    /**
     * 二進制數(shù)組相加
     * @param array $args 二進制數(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]不夠長就頭插補齊
        $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); // 計算完后還有進位則加長存放
         }
        return array_slice($args[0], -32); // 計算結(jié)果只保留32位
    }

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

    /**
     * 打印二進制數(shù)組
     * @param array $bits 二進制數(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;
    }

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

    /**
     * 二進制數(shù)組轉(zhuǎn)化為十六進制
     * @param array $bits 二進制數(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)二進制

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

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

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

    “hello world”

    01101000 01100101 01101100 01101100  01101111 00100000 01110111 01101111

    01110010 01101100 01100100

    /**
     * 步驟一:將字符串轉(zhuǎ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

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

    $bits

    01101000 01100101 01101100 01101100  01101111 00100000 01110111 01101111

    01110010 01101100 01100100 1

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

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

    在二進制數(shù)組的末尾添加 0 以使得整個二進制數(shù)組的個數(shù)剛好是 512 的倍數(shù)。需要注意的是,二進制數(shù)組的最末尾要預(yù)留 64 位用于存放原始二進制的長度。也就是一開始將字符串轉(zhuǎn)換成二進制時的長度,我們在 步驟一 中將這個長度值保存到了 $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 位用于存儲原始字符串的長度    ]

    /**
     * 步驟三:在數(shù)據(jù)末尾添加0,確保二進制的個數(shù)是512的倍數(shù),最后預(yù)留64位用于存儲原始長度信息
     */
     
    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++;
    }
        }
    }

    步驟四:追加原始長度信息

    把之前記錄的原始數(shù)據(jù)長度 $originLen 轉(zhuǎn)換為 64 位的二進制追加到 $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

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

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

    經(jīng)過 步驟四 之后,$bits 二進制數(shù)組的個數(shù)已經(jīng)是 512 的倍數(shù),現(xiàn)在以每 512 位分為一個區(qū)塊,然后在每個區(qū)塊末尾填充 0,讓每個區(qū)塊的大小變成 2048 位。每個區(qū)塊的 2048 位數(shù)據(jù)以 32 位作為一行,那么就有 64 行。由于 "hello world" 數(shù)據(jù)比較短,我們就只有一個區(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
    /**
     * 步驟五:每一個512位切分區(qū)塊,在區(qū)塊末尾填充0,使得每個區(qū)塊位數(shù)為2048位,經(jīng)計算
     * 每個區(qū)塊還需要添加48x32個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ù)修改

    上一步中我們給每一個區(qū)塊末尾添加了很多 0,在這一步中,通過一些位操作將這些數(shù)據(jù)進一步調(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 是每個區(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(相加得到的值如果超過 32 位,則抹去高位)
    /**
     * 步驟六:針對每一個2048位區(qū)塊處理:以32位為一行,總共有64行,修改【16-63】行的數(shù)據(jù),
     * 這【16-63】行就是上一步新增的48x32個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的長度超過了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)每一個區(qū)塊的每一行,通過 與 非 異或 等操作將信息壓縮到 $a、$b、$c、$d、$e、$f、$g、$h 中,最后將 $a、$b、$c、$d、$e、$f、$g、$h 的值與原始常量 H[0-7] 相加,拼接相加后的二進制結(jié)果 h0~h7 并轉(zhuǎ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 行的處理為例,列出了變量計算結(jié)果方便大家對照調(diào)試:

    計算結(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));
    }

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

    總結(jié)

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

    標(biāo)簽: PHP
    相關(guān)文章:
    主站蜘蛛池模板: 礼堂椅厂家|佛山市艺典家具有限公司| 压装机-卧式轴承轮轴数控伺服压装机厂家[铭泽机械] | 茶叶百科网-茶叶知识与茶文化探讨分享平台| 电力测功机,电涡流测功机,磁粉制动器,南通远辰曳引机测试台 | 昆明网络公司|云南网络公司|昆明网站建设公司|昆明网页设计|云南网站制作|新媒体运营公司|APP开发|小程序研发|尽在昆明奥远科技有限公司 | LNG鹤管_内浮盘价格,上装鹤管,装车撬厂家-连云港赛威特机械 | 环氧乙烷灭菌器_压力蒸汽灭菌器_低温等离子过氧化氢灭菌器 _低温蒸汽甲醛灭菌器_清洗工作站_医用干燥柜_灭菌耗材-环氧乙烷灭菌器_脉动真空压力蒸汽灭菌器_低温等离子灭菌设备_河南省三强医疗器械有限责任公司 | 压砖机_电动螺旋压力机_粉末成型压力机_郑州华隆机械tel_0371-60121717 | 黑龙江「京科脑康」医院-哈尔滨失眠医院_哈尔滨治疗抑郁症医院_哈尔滨精神心理医院 | 运动木地板价格,篮球馆体育运动木地板生产厂家_欧氏地板 | 氟塑料磁力泵-不锈钢离心泵-耐腐蚀化工泵厂家「皖金泵阀」 | 耐火浇注料-喷涂料-浇注料生产厂家_郑州市元领耐火材料有限公司 耐力板-PC阳光板-PC板-PC耐力板 - 嘉兴赢创实业有限公司 | 全自动五线打端沾锡机,全自动裁线剥皮双头沾锡机,全自动尼龙扎带机-东莞市海文能机械设备有限公司 | 诺冠气动元件,诺冠电磁阀,海隆防爆阀,norgren气缸-山东锦隆自动化科技有限公司 | 旗杆生产厂家_不锈钢锥形旗杆价格_铝合金电动旗杆-上海锥升金属科技有限公司 | 二氧化碳/活性炭投加系统,次氯酸钠发生器,紫外线消毒设备|广州新奥 | 渗透仪-直剪仪-三轴仪|苏州昱创百科 | 螺杆式冷水机-低温冷水机厂家-冷冻机-风冷式-水冷式冷水机-上海祝松机械有限公司 | 免费网站网址收录网_海企优网站推荐平台| 反渗透水处理设备|工业零排放|水厂设备|软化水设备|海南净水设备--海南水处理设备厂家 | 爱佩恒温恒湿测试箱|高低温实验箱|高低温冲击试验箱|冷热冲击试验箱-您身边的模拟环境试验设备技术专家-合作热线:400-6727-800-广东爱佩试验设备有限公司 | 品牌策划-品牌设计-济南之式传媒广告有限公司官网-提供品牌整合丨影视创意丨公关活动丨数字营销丨自媒体运营丨数字营销 | ★店家乐|服装销售管理软件|服装店收银系统|内衣店鞋店进销存软件|连锁店管理软件|收银软件手机版|会员管理系统-手机版,云版,App | 首页-瓜尔胶系列-化工单体系列-油田压裂助剂-瓜尔胶厂家-山东广浦生物科技有限公司 | 风电变桨伺服驱动器-风电偏航变桨系统-深圳众城卓越科技有限公司 | 恒温恒湿箱(药品/保健品/食品/半导体/细菌)-兰贝石(北京)科技有限公司 | 不锈钢/气体/液体玻璃转子流量计(防腐,选型,规格)-常州天晟热工仪表有限公司【官网】 | 磁力抛光机_磁力研磨机_磁力去毛刺机_精密五金零件抛光设备厂家-冠古科技 | 仓储货架_南京货架_钢制托盘_仓储笼_隔离网_环球零件盒_诺力液压车_货架-南京一品仓储设备制造公司 | 南京技嘉环保科技有限公司-杀菌除臭剂|污水|垃圾|厕所|橡胶厂|化工厂|铸造厂除臭剂 | 全自动烧卖机厂家_饺子机_烧麦机价格_小笼汤包机_宁波江北阜欣食品机械有限公司 | 临海涌泉蜜桔官网|涌泉蜜桔微商批发代理|涌泉蜜桔供应链|涌泉蜜桔一件代发 | 广州展览设计公司_展台设计搭建_展位设计装修公司-众派展览装饰 广州展览制作工厂—[优简]直营展台制作工厂_展会搭建资质齐全 | 变频器维修公司_plc维修_伺服驱动器维修_工控机维修 - 夫唯科技 变位机,焊接变位机,焊接变位器,小型变位机,小型焊接变位机-济南上弘机电设备有限公司 | 短信群发平台_群发短信软件_短信营销-讯鸽科技 | SEO网站优化,关键词排名优化,苏州网站推广-江苏森歌网络 | 小青瓦丨古建筑瓦丨青瓦厂家-宜兴市徽派古典建筑材料有限公司 | 飞扬动力官网-广告公司管理软件,广告公司管理系统,喷绘写真条幅制作管理软件,广告公司ERP系统 | 钢格板|热镀锌钢格板|钢格栅板|钢格栅|格栅板-安平县昊泽丝网制品有限公司 | 金属清洗剂,防锈油,切削液,磨削液-青岛朗力防锈材料有限公司 | 心肺复苏模拟人|医学模型|急救护理模型|医学教学模型上海康人医学仪器设备有限公司 |