zoukankan      html  css  js  c++  java
  • 密码学那些事———SHA-512及其C++实现

    SHA-512及其C++实现

    转载请注明出处

    一、引言

      相信大家对于哈希压缩加密算法应该不陌生,在我们用微信或者支付宝接口的时候经常会遇到用这类算法加密,以验证数据的完整性。可以说这类算法无处不在,那这些算法的原理是什么呢?

    今天我们以SHA-512为例来说明。

    二、简单介绍

      SHA (Secure Hash Algorithm,译作安全散列算法) 是美国国家安全局 (NSA) 设计,美国国家标准与技术研究院 (NIST) 发布的一系列密码散列函数。我们将要介绍的SHA-512就是SHA2系列的一种,到目前为止,SHA系列已经发展到SHA3,

    其中SHA1早在2005年就被证明是不安全的,已经有了破解的办法,谷歌也在很多年前就不再使用SHA1,当前主流的是SHA2。

    下图是一些简单的介绍。(转自维基)

    公式:

    h = Hash(message)

    Hash:哈希函数

    message:不超过最大消息长度的任意长度消息

    h: 相应位数的密文

    安全哈希算法是一种超损压缩:将一个非常大的数据压缩到固定位长的数据,因而这是一个不可逆的算法。

    为什么不可逆?

      在不知算法具体流程的情况下,我们来考虑类似的问题。

      123456789abc  => 123 请问给你123,你怎么推出原字符是什么?

      同时你会说一个函数那我们找它逆函数,再映射回去不就好了?那么好我告诉你我们的原函数是:截断后面的bits,只留下开头的3个。那么你现在可以帮我找到原消息了吗?

      显然这样有2^n种可能。因而我们认为安全哈希函数是不可逆的。这也就可以保证我们的安全行了。

      

    三、算法描述

      一、处理原文

        1、消息bits(二进制)化,将消息化为二进制(不一定直接一次性转为二进制,在后面我贴的代码里是动态转换的)。

        2、填充字长:

          (在我们后面的处理过程中,都是以1024bits(128B)为一次操作的最小单位的,因而消息得满足128B对齐)

    896 = length(message)%1024

                 也就是满足填充后消息长度(bits)除1024取余数等于896(1024-128=896)

        3、填充

           第一位填充1,其他位填充0,满足除余为896后,还剩下128bits.这个用于存消息长度。(SHA512中为128位,因而此算法最长消息为2^128-1)

      二、设置初始值

        SHA512算法的结果长度为512位,按每组64位分成8组,这8组结果是由8个初始值A,B,C,D,E,F,G,H经过不断演变得到的。这8个初始值是:

            A = 0x6a09e667f3bcc908ULL;
            B = 0xbb67ae8584caa73bULL;
            C = 0x3c6ef372fe94f82bULL;
            D = 0xa54ff53a5f1d36f1ULL;
            E = 0x510e527fade682d1ULL;
            F = 0x9b05688c2b3e6c1fULL;
            G = 0x1f83d9abfb41bd6bULL;
            H = 0x5be0cd19137e2179ULL;

      三、循环加工

        (这就是算法最核心的地方,我们形象的把它称为哈希工厂)

         下面我们看图说话

         

        图中A-H哈希的8个分组,每次循环从旧的中产生新的,一共得循环多少次呢?

        主循环次数 = 消息长度/1024

        每次主循环中又保存80次子循环

        上图就是表达了单次子循环的流程

        主要操作

            

            

            

            

            >>>表示循环右移

            田:加法

            对应C语言表达式子:

              #define Ch( x, y, z )     (z ^ (x & (y ^ z)))
              #define Maj(x, y, z )     (((x | y) & z) | (x & y))
              #define S( x, n )         ROR64( x, n )
              #define R( x, n )         (((x)&0xFFFFFFFFFFFFFFFFULL)>>((unsigned long long)n))
              #define Sigma0( x )       (S(x, 28) ^ S(x, 34) ^ S(x, 39))
              #define Sigma1( x )       (S(x, 14) ^ S(x, 18) ^ S(x, 41))
              #define Gamma0( x )       (S(x, 1) ^ S(x, 8) ^ R(x, 7))
              #define Gamma1( x )       (S(x, 19) ^ S(x, 61) ^ R(x, 6))

          W,K是两个常量

          其中W是计算出的,具体计算看代码。

          K:预先给出的80个常量

          相信大家有了上图后已经明白了核心操作。要是还不明白建议根据附录中的代码进一步理解。    

        四、拼接结果

          将最后的8个常量一次拼接则得到结果。

     

    C语言实现(C++)

    SHA512.h

    #ifndef SHA512_H
    #define SHA512_H
    
    //////////////////////////////////////////////////////////
    // SHA512_CB(control block)                             //
    // SHA512_CB:SHA512控制块,包含算法运算过程中将用到的信息//
    // count[2]:记录128位的数字长度(两个64位)             //
    // state[8]:A-H八个初始常量(64bit)                      //
    // buffer[128]:用于每次运算的1024bit                      //
    //                                                      //
    //////////////////////////////////////////////////////////
    typedef struct
    {
        unsigned long long count[2];
        unsigned long long state[8];
        unsigned char buffer[128];
    } SHA512_CB;
    
    // 用于补齐的数,最多补128字节也就是1024bit
    unsigned char PADDING[] = {
        0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    };
    
    // 每次子循环中用到的常量
    // 后面加ULL表示long long
    static const unsigned long long K[80] = {
        0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
        0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
        0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
        0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
        0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
        0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
        0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
        0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
        0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
        0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
        0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
        0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
        0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
        0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
        0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
        0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
        0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
        0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
        0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
        0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
    };
    // 初始化函数,初始化SHA_CB的各个值
    void SHA512Init(SHA512_CB *context);
    
    // 将数据加入
    void SHA512Update(SHA512_CB *context, unsigned char *input, unsigned long long inputlen);
    
    // 处理完最后再调用,这个处理尾数
    void SHA512Final(SHA512_CB *context, unsigned char digest[32]);
    
    // 加密处理函数:Hash加密的核心工厂
    void SHA512Transform(unsigned long long state[8], unsigned char block[128]);
    
    // 编码函数:将整型编码转为字符
    void SHA512Encode(unsigned char *output, unsigned long long *input, unsigned long long len);
    
    // 解码函数:将字符数组保存的编码转为整型
    void SHA512Decode(unsigned long long *output, unsigned char *input, unsigned long long len);
    #endif

    SHA512.c

    #include <memory.h>
    #include <string.h>
    #include <stdio.h>
    #include "SHA512.h"
    
    
    
    // 循环右移(64位)
    #define ROR64( value, bits ) (((value) >> (bits)) | ((value) << (64 - (bits))))
    
    //////////////////////////////////////////////////////
    //                                                    //
    // Ch,:Maj操作                                        //
    // S:循环右移    R:同2**128除余右移                    //
    // Sigma0:Sigma0函数                                //
    // Sigma1:Sigma2函数                                //
    // Gamma0:Gamma0函数                                //
    // Gamma1:Gamma1函数                                //
    //////////////////////////////////////////////////////
    
    #define Ch( x, y, z )     (z ^ (x & (y ^ z)))
    #define Maj(x, y, z )     (((x | y) & z) | (x & y))
    #define S( x, n )         ROR64( x, n )
    #define R( x, n )         (((x)&0xFFFFFFFFFFFFFFFFULL)>>((unsigned long long)n))
    #define Sigma0( x )       (S(x, 28) ^ S(x, 34) ^ S(x, 39))
    #define Sigma1( x )       (S(x, 14) ^ S(x, 18) ^ S(x, 41))
    #define Gamma0( x )       (S(x, 1) ^ S(x, 8) ^ R(x, 7))
    #define Gamma1( x )       (S(x, 19) ^ S(x, 61) ^ R(x, 6))
    
    #define Sha512Round( a, b, c, d, e, f, g, h, i )       
         t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i];   
         t1 = Sigma0(a) + Maj(a, b, c);                    
         d += t0;                                          
         h  = t0 + t1;
    
    void SHA512Init(SHA512_CB *context) {
        context->count[0] = 0;
        context->count[1] = 0;
        context->state[0] = 0x6a09e667f3bcc908ULL;
        context->state[1] = 0xbb67ae8584caa73bULL;
        context->state[2] = 0x3c6ef372fe94f82bULL;
        context->state[3] = 0xa54ff53a5f1d36f1ULL;
        context->state[4] = 0x510e527fade682d1ULL;
        context->state[5] = 0x9b05688c2b3e6c1fULL;
        context->state[6] = 0x1f83d9abfb41bd6bULL;
        context->state[7] = 0x5be0cd19137e2179ULL;
    }
    
    void SHA512Update(SHA512_CB *context, unsigned char *input, unsigned long long inputlen) {
        unsigned long long index = 0, partlen = 0, i = 0; // i记录input的当前位置(初始为0)
        index = (context->count[1] >> 3) & 0x7F;  //index:总字长除127(11111111)取余后的余数
        partlen = 128 - index;  //partlen:同128相差的长度
        context->count[1] += inputlen << 3;  //更新count
    
        // 统计字符的bit长度,如果小于说明类型溢出了(64bit)无法装下了
        // 由于最后留下128bit填充字符长度,因而必须引入count[1]保存
        // 64bit+64bit=128bit
        if (context->count[1] < (inputlen << 3))
            context->count[0]++;
        //右移动61位后就是count[0]应该记录的值。(左移3位,溢出的就是右移动61位的)
        context->count[0] += inputlen >> 61;
    
        //////////////////////////////////////////////////////////
        //                                                        //
        //     如果此次更新的长度,大于原长度同128做差的值,    //
        //     .ie. 加上刚更新的长度满足了128Bytes(1024位)      //
        //     因而可以进行一次加密循环                            //
        //                                                        //
        //////////////////////////////////////////////////////////
    
        if (inputlen >= partlen)
        {
            //将缺的partlen个字节数据加入缓冲区
            memcpy(&context->buffer[index], input, partlen);
            SHA512Transform(context->state, context->buffer);
    
            // 如果输入的字,还可以进行(还有整128字的)就继续进行一次加密循环
    
            for (i = partlen; i + 128 <= inputlen; i += 128)
                SHA512Transform(context->state, &input[i]);
            // 将当前位置设为0
            index = 0;
        }
        else
        {
            i = 0;
        }
        // 重新设置buffer区(处理过的字被覆盖成新字)
        memcpy(&context->buffer[index], &input[i], inputlen - i);
    }
    
    void SHA512Final(SHA512_CB *context, unsigned char digest[64]) {
        unsigned int index = 0, padlen = 0;
        unsigned char bits[16]; // 记录字长信息
        index = (context->count[1] >> 3) & 0x7F; // 字长除127(11111111)取余长度
        padlen = (index < 112) ? (112 - index) : (240 - index); // 补齐的字长
        SHA512Encode(bits, context->count, 16);
        SHA512Update(context, PADDING, padlen);
        SHA512Update(context, bits, 16);
        SHA512Encode(digest, context->state, 64);
    }
    
    void SHA512Encode(unsigned char *output, unsigned long long *input, unsigned long long len) {
        unsigned long long i = 0, j = 0;
        while (j < len)
        {
            output[j+7] = input[i] & 0xFF;
            output[j + 6] = (input[i] >> 8) & 0xFF; //0xFF:11111111
            output[j + 5] = (input[i] >> 16) & 0xFF;
            output[j + 4] = (input[i] >> 24) & 0xFF;
            output[j + 3] = (input[i] >> 32) & 0xFF;
            output[j + 2] = (input[i] >> 40) & 0xFF;
            output[j + 1] = (input[i] >> 48) & 0xFF;
            output[j] = (input[i] >> 56) & 0xFF;
            i++;
            j += 8;
        }
    }
    
    
    void SHA512Decode(unsigned long long *output, unsigned char *input, unsigned long long len) {
        unsigned long long i = 0, j = 0;
        while (j < len)
        {
            output[i] = ((unsigned long long)input[j+7]) |
                        ((unsigned long long)input[j + 6] << 8) |
                        ((unsigned long long)input[j + 5] << 16) |
                        ((unsigned long long)input[j + 4] << 24) |
                        ((unsigned long long)input[j + 3] << 32) |
                        ((unsigned long long)input[j + 2] << 40) |
                        ((unsigned long long)input[j + 1] << 48) |
                        ((unsigned long long)input[j] << 56);
            i++;
            j += 8;
        }
    }
    
    void SHA512Transform(unsigned long long state[8], unsigned char block[128]) {
        unsigned long long S[8];
        unsigned long long W[80];
        unsigned long long t0;
        unsigned long long t1;
        int i = 0;
        printf("
    填充后(1024bits):
    0x");
        for(int index=0;index<128;index++){
            printf("%02x", block[index]);
        }
        printf("
    ");
        // 把state的值复制给S
        for ( i = 0; i < 8; i++ )
        {
            S[i] = state[i];
        }
    
        // 将字符数组保存的编码转为unsigned long long
        SHA512Decode(W, block, 128);
    
        for ( i = 16; i < 80; i++ )
        {
            W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
        }
    
        for ( i = 0; i < 80; i += 8 )
        {
            Sha512Round(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i + 0);
            Sha512Round(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], i + 1);
            Sha512Round(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], i + 2);
            Sha512Round(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], i + 3);
            Sha512Round(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], i + 4);
            Sha512Round(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], i + 5);
            Sha512Round(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], i + 6);
            Sha512Round(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], i + 7);
        }
        printf("
    ");
        printf("A:%I64u
    ", S[0]);
        printf("B:%I64u
    ", S[1]);
        printf("C:%I64u
    ", S[2]);
        printf("D:%I64u
    ", S[3]);
        printf("E:%I64u
    ", S[4]);
        printf("F:%I64u
    ", S[5]);
        printf("G:%I64u
    ", S[6]);
        printf("H:%I64u
    ", S[7]);
        printf("
    ");
        // Feedback
        for ( i = 0; i < 8; i++ )
        {
            state[i] = state[i] + S[i];
        }
    }
    
    int main(int argc, char* argv[])
    {
    
        int i;
        unsigned char input[] = "jack";
        printf("输入字符串的十六进制: 0x");
        for(unsigned int i=0;i<strlen((char*)input);i++){
            printf("%02x", input[i]);
        }
        printf("
    ");
    
        unsigned char sha512Code[64];
    
        SHA512_CB sha512;
    
        SHA512Init(&sha512);
        SHA512Update(&sha512, input, strlen((char *)input));
        SHA512Final(&sha512, sha512Code);
    
        //Md5加密后的32位结果
        printf("
    加密前:%s
    加密后128位:", input);
        for (i = 0; i < 64; i++)
        {
            printf("%02x", sha512Code[i]);
        }
    
        getchar();
    
        return 0;
    }

     转载请注明出处。

  • 相关阅读:
    springMVC(5)---导入excel文件数据到数据库
    springMVC(4)---生成excel文件并导出
    springMVC(3)---利用pdf模板下载
    springMVC(1)---@RequestMapping详解
    springMVC(2)---获取前段数据
    【JS】---5 JS通过事件隐藏显示元素
    【JS】---4用JS获取地址栏参数方法
    【功能代码】---3 JS判断字符串是否包含某个字符串
    基于maven的ssm框架整合
    java提高(9)---HashMap解析
  • 原文地址:https://www.cnblogs.com/jake9402/p/7801969.html
Copyright © 2011-2022 走看看